Posts tagged ‘google-collections’

This blog is a contitunation of my previous entry (polish only sorry) in which I presented a solution for hiding certain operations on a collection using jdk’s dynamic proxy mechanism. It consited of MethodInterceptr which checked if specific method was called and in such a case it reported and error using RuntimeException.

With this entry I’d like to present a different approach using google collection ForwardingObjects. It’s parent for all types of wrappers – one for each collection type and the advantage of using wrapper is that hey’re all designed as abstract classes implementing interface for given type of Collection, in case of Set it’s ForwardingSet (to continue older example) and the only thing you need to do as implementor is to telli wrapper how it can find its delegate by providing a delegate method.

Below you can find previous example rewritten to use ForwardingSet with disabled clear method. Last but not least – this version is waaay much cleaner 馃檪 and you also let compiler do its job – if you check the previous post you’ll notice that there are two versions of this solution. The original one contained a but because I made a typo and the code checks for call to clean method instead of clear…

import java.util.Set;
import com.google.common.collect.ForwardingSet;

class StructElement3 {

	private class ForwardingSetNoClear extends ForwardingSet<String> {

		private Set<String> delegate;
		public ForwardingSetNoClear(Set<String> delegate) {
			this.delegate = delegate;
		}

		@Override
		protected Set<String> delegate() {
			return delegate;
		}

		@Override
		public void clear() {
			throw new UnsupportedOperationException("Cannot call clear");
		}
	}

	public StructElement3(Set<String> obj) {
		Set<String> forwardingSet =  new ForwardingSetNoClear(obj);

		// yeah yeah keep talking...
		forwardingSet.clear();
	}

}

Klasa Joiner dost臋pna w pakiecie google-collections oszcz臋dza programi艣cie niezno艣nego sprawdzania pierwszego, b膮d藕 ostatniego elementu kolekcji/tablicy w celu sprawdzenia czy wymagane jest dodanie separatora, co znacznie poprawia czytelno艣膰 kodu.

Jednak podczas korzystania z tej klasy natrafi艂em na drobny problem – podczas 艂膮czenia element贸w przy pomocy metody join kolejne kawa艂ki s膮 dodawane poprzez wywo艂anie metody toString na obecnie przetwarzanym elemencie (chyba 偶e jest to CharSequence lub jego pochodne). W moim przypadku elementem kolekcji by艂 javaBean i chcia艂em zamiast wywo艂ania metody toString zawo艂a膰 sobie jego metod臋 getId, no i niestety jedyna rad臋 jak膮 znalaz艂em w dokumentacji, by艂o przekszta艂cenie kolekcji bean贸w w kolekcje typ贸w prostych (lub ich zawijaczy) a nast臋pnie uruchomienie na tej偶e kolekcji Joiner’a, mniej wi臋cej tak:


// [ctrl] + [shift] + O

interface MyBean {

public Integer getId();

}
		// [ctrl] + [shift] + O

		List<MyBean> myBeans = new ArrayList<MyBean>();

		// w kolekcji co艣tam przysz艂o sk膮d艣 indziej

		// teraz zamieniamy j膮 na kolecj臋 Integer贸w
		List<Integer> ids = Lists.transform(myBeans, new Function<MyBean, Integer>() {
			public Integer apply(MyBean b) {
				return b.getId();
			}
		});

		Joiner commaSeparated = Joiner.on(",");
		String commaSeparatedIds = commaSeparated.join(ids);

No i wszystko 艂adnie i pi臋knie, ale dlaczego nie mog臋 sobie od razu wywo艂a膰 metody join przekazuj膮c jej jako pierwszy parametr funkcji konwertuj膮cej mojego beana na string. No to do dzie艂a – b臋dziemy potrzebowali tak naprawd臋 3 metody: appendTo, join, no to po kolei:

	public final <T> String join(Function<T, String> f, Iterable<T> parts) {
		return appendTo(f, new StringBuilder(), parts).toString();
	}
	public final <T> StringBuilder appendTo(Function<T, String> f, StringBuilder builder, Iterable<T> parts) {
		try {
			appendTo(f, (Appendable) builder, parts);
		} catch (IOException impossible) {
			throw new AssertionError(impossible);
		}
		return builder;
	}

I ostatnia

	public <A extends Appendable, T> A appendTo(Function<T, String> f, A appendable, Iterable<T> parts) throws IOException {
		checkNotNull(appendable);
		Iterator<T> iterator = parts.iterator();
		if (iterator.hasNext()) {
			appendable.append(f.apply(iterator.next()));
			while (iterator.hasNext()) {
				appendable.append(separator);
				appendable.append(f.apply(iterator.next()));
			}
		}
		return appendable;
	}

Teraz jedyne co trzeba zrobi膰 to przekaza膰 funkcj臋 jako pierwszy parametr:

		// [ctrl] + [shift] + O

		List<MyBean> myBeans = new ArrayList<MyBean>();

		// w kolekcji co艣tam przysz艂o sk膮d艣 indziej

		Joiner commaSeparated = Joiner.on(",");
		String commaSeparatedIds = commaSeparated.join(new Function<MyBean, String>() {
			public String apply(MyBean b) {
				return ""+b.getId();
			}
		}, myBeans);

Pozostaje tylko ma艂y zgrzyt – konstruktor Joiner’a zosta艂 zadeklarowany jako prywatny, wi臋c niestety nie da si臋 po tej klasie podziedziczy膰 – pozostaje copy-paste 馃檨