Strona główna > Java > Dalej dziwne konstrukcje

Dalej dziwne konstrukcje

Wpadłem ostatnio w nastrój tworzenia niewielkich konstrukcji, które mogą zamknąć, w „zgrabniejszy” sposób, koncepcje jakie stoją za danym fragmentem kodu. Zaczęło się to chyba z tym postem.
Pisanie w ten sposób jest próbą wyrażenia inaczej czegoś co i tak najczęściej ląduje w statycznych metodach narzędziowych.
Głównym kryterium jest tu jak najlepsze wyrażenie koncepcji, kosztem wydajności, czy czasem nieporadności konstrukcji – stąd te dziwne konstrukcje😉.

Pierwszym przykładem jest swich na stringach. Nie będę tu oceniał zasadności takiej struktury. Podobno ma wejść to z nową wersją javy, ale jak to pokaże niże, można to mieć już teraz.

Ważne jest uświadomienie sobie czego się chcę. Czy słówko kluczowe akceptujące konkretny rodzaj argumentów? Czy realizacje określonego sposobu kodowania w oparciu o dany rodzaj argumentu?

Ja spróbowałem zrealizować tą drugą koncepcje. I oto wyniki:

		// po starem tylko liczby (enum także)
		int key = 1;
		switch (key) {
		case 1:
			System.out.println("1");
		case 2:
		case 3:
			System.out.println("2,3");
			break;
		default:
			System.out.println("default");
			break;
		}
		
		// po nowemu String (choć może być teraz cokolwiek)
		String sKey = "1";
		StringSwitch sSwitch = new StringSwitch(sKey);
		if (sSwitch.isCase("1")) {
			System.out.println("1");
		}
		if (sSwitch.isCase("2") || sSwitch.isCase("3")) {
			System.out.println("2,3");
			sSwitch.doBreak();
		}
		if (sSwitch.isDefault()) {
			System.out.println("default");
		}

Myślę, że widać co jest tu realizowane. To jak wygląda klasa StringSwitch także jest łatwo sobie wyobrazić. U mnie zajęła około 20 linii, ale i tak można ją jeszcze poprawić (np. dodać coś takiego sSwitch.isCase("2","3")). Kluczowe jest tu nadanie odpowiednich nazw.

Podbudowany tym sukcesem zabrałem się za bardziej „obrazoburcza” konstrukcje. Próba realizacji idei goto wyszła mniej zgrabnie, dlatego też nie będę tego tu umieszczał. Było to dalsze rozwinięcie tego co zostało użyte w switch_u, wsparte o użycie pętli😉.

Poszedłem dalej i spróbowałem czy da się wyrazić inne konstrukcje. Przy próbie wyrażenia for za pomocą obiektów zrobiło się ciekawie.
Na początek aby pisać jak najmniej kodu wykorzystałem konstruktor w trochę niestandardowy sposób

		Collection<String> strings = create("Pierwszy", "drugi");
		
		new ForEach<String>(strings) {
			void forEachItem(String item) {
				System.out.println("item = "+item);
			}
		};

Mam nadzieje, że widać o co chodzi (wszytko będzie załączone w ostatnim listingu). Może wprowadza to więcej zamieszania niż zwykł foreach, ale to tylko początek do innych konstrukcji🙂.
Dzięki takiemu potraktowaniu tego wyrażenia można go wywoływać wielokrotnie dla różnych kolekcji.

		ForEach<String> forEach = new ForEach<String>() {
			void forEachItem(String item) {
				System.out.println("item = >"+item+"<");
			}
		};
		strings.add("trzeci");
		forEach.doForEach(strings);

Można to rozwinąć w kierunku bardziej specjalistycznych iteracji

		Repack<String,String> repack = new Repack<String, String>(){
			String repackItem(String item) {
				return "!"+item+"!";
			}
		};
		Collection<String> newStrings = repack.doRepack(strings);

Powyższy kod zdarzało mi się już wykorzystywać do przepakowywania elementów kolekcji z jednej klasy do innej.
Także pokusiłem się o realizacje koncepcji zaczerpniętych z innych języków. Może jest to przegadane wyrażenie lambda, ale chyba widać o co chodzi.

		// reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]);
		Collection<Integer> integers = create(1, 2, 3, 4, 5);
		
		Integer reduce = new Reduce<Integer>() {
			Integer reduceItem(Integer x, Integer y) {
				return x + y;
			};
		}.doReduce(integers);

Koncepcja reduce nie jest do końca uniwersalnie napisana – obsługuje tylko dwuargumentową funkcje.

Podsumowując zabawy z foreach. Do bezpośredniego użycia w kodzie może razić umieszczanie klas anonimowych, zwłaszcza gdy nie ma zysku na liczbie linii kodu. Jednak jako dodatkowy poziom abstrakcji, może łatwo komunikować co naprawdę jest ważne. Tutaj powinno zwracać uwagę na to że jest to jakiś kawałek kod w metodzie klasy anonimowej, który to będzie iterowany poprzez daną kolekcje. Dobry przykład to metoda create. Tu miało służyć do stworzenia kolekcji z przekazanej tablicy (co już po fakcie dowiedziałem się, że jest w standardzie – List list = Arrays.asList(array);). Jednak powyższy kod można zastosować do bardziej złożonych przypadków, przykład to: tworzenie kolekcji BigDecimal na bazie Integer lub tworzenia kolekcji na podstawie elementów granicznych i definicji kodu tworzącego następny element.

Cały kod związany z przykładami dla foreach poniżej:

public class ForEachTest{
	public static void main(String[] args) {
		Collection<String> strings = create("Pierwszy", "drugi");
		
		System.out.println("\n-- prosty forEach");
		new ForEach<String>(strings) {
			void forEachItem(String item) {
				System.out.println("item = "+item);
			}
		};
		
		System.out.println("\n-- odroczony forEach");
		ForEach<String> forEach = new ForEach<String>() {
			void forEachItem(String item) {
				System.out.println("item = >"+item+"<");
			}
		};
		strings.add("trzeci");
		forEach.doForEach(strings);
		
		System.out.println("\n-- przepakowanie");
		Repack<String,String> repack = new Repack<String, String>(){
			String repackItem(String item) {
				return "!"+item+"!";
			}
		};
		forEach.doForEach(repack.doRepack(strings));
		
		System.out.println("\n-- reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])");
		Collection<Integer> integers = create(1, 2, 3, 4, 5);
		
		Integer reduce = new Reduce<Integer>() {
			Integer reduceItem(Integer x, Integer y) {
				return x + y;
			};
		}.doReduce(integers);
		
		System.out.println(reduce);
	}
	
	private static <T> Collection<T> create(T ... items) {
		return new Repack<T, T>() {
			T repackItem(T item) {
				return item;
			}
		}.doRepack(items);
	}

	private static abstract class Reduce<E>{
		public Reduce() {
		}
		public E doReduce(Collection<E> col){
			E value = null;
			for (E item : col) {
				if (value == null) {
					value = item;
				} else {
					value = reduceItem(value, item);
				}
			}
			return value;
		}
		abstract E reduceItem(E value, E item);
	}
	
	private static abstract class Repack<FROM,TO>{
		public Collection<TO> doRepack(Collection<FROM> col){
			Collection<TO> list = createCollection(col.size());
			for (FROM item : col) {
				list.add(repackItem(item));
			}
			return list;
		}
		public Collection<TO> doRepack(FROM ... col){
			Collection<TO> list = createCollection(col.length);
			for (FROM item : col) {
				list.add(repackItem(item));
			}
			return list;
		}
		private Collection<TO> createCollection(int size) {
			// lista, ale mozna nadpisac to aby zmienic na porzadany typ
			List<TO> list = new ArrayList<TO>(size);
			return list;
		}
		abstract TO repackItem(FROM item);
	}
	
	private static abstract class ForEach <E>{
		public ForEach(Collection<E> col) {
			doForEach(col);
		}
		public ForEach() {
		}
		public void doForEach(Collection<E> col){
			for (E item : col) {
				forEachItem(item);
			}
		}
		abstract void forEachItem(E item);
	}
}
Kategorie:Java Tags: ,
  1. Brak komentarzy.
  1. No trackbacks yet.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

%d bloggers like this: