NIL - Not Intelligent Language
Czasami nachodzi mnie ochota na myślenie o NIL'u - połączeniu cech Javy, Rubiego i C++. Do cech należałyby:
- Wielodziedziczenie
- Dynamiczne interfejsy (zaraz wytłumaczę o co mi chodzi)
- Rozszerzanie klas (jak w Ruby) i możliwość mrożenia
- Zaawansowana kontrola dostępu
- Możliwość korzystania z Proxy
- Możliwość korzystania z klas .Net i Javy (opcjonalnie)
- Typy generyczne
Wielodziedziczenie
Oprócz tego, że wszystko dziedziczy z klasy Object to można stosować wielodziedziczenie. Nie wymyśliłem jeszcze jak rozwiązać konstruktory, ale to chyba jedyna przeszkoda (kolejność klas w deklaracji jest o tyle ważna, że VM wywołuje pierwszą pasującą metodę idąc od lewej do prawej)
Wielodziedziczenie rzadko się przydaje, ale czasami znacznie upraszcza sprawę (szczególni, że wtedy dużo kodu jest zajmowanego na obejście tego problemu
Dynamiczne interfejsy
Deklarujemy taki interfejs:
public interface Xyz {
void doSomething();
}
I mamy klasy (z biblioteki):
public class Abc {
void doSomething() { . . . }
}
public class Def {
void doSomething() { . . . }
}
I... Nie potrzebujemy zmieniać kodu klasy taki kod jest poprawny w Nil'u
public interface Xyz2 extends Xyz {
optional void doSomethingElse();
}
Obie te klasy implementują także ten interfejs - tylko, że przy próbie wywołania (za pomocą interfejsu) UnsupportedOperationException, czy czyms w tym rodzaju
Xyz xyz = new Abc();
xyz = new Def();
Rozszerzanie klas
Czasami potrzebujemy jakiejś metody, której nie ma standardowo. Pozostaje nam:
- Poszukać klasy, która za nas to zrobi
- Napisać taką klasę
Podam banalny przykład: Potrzebowałem metodę, która wczyta linię kodu (zakończoną ;). Niestety split na Stringu daje dwie linie dla takiej linii:
Oczywiście, mogę napisać klasę rozszerzającą BufferedReader, ale gdzie ją umieścić (pakiet utils z jedną klasą? pakiet, gdzie to zupełnie nie pasuje?)
Kontrola dostępu
Oprócz 4 znanych z Javy modyfikatorów dostępu można stosować znaczniki (Adnotacje, czy jak je nazwiemy) np. @Friend
public class Ghi {
@Friend(Abc, Def.doSomething)
private void doSomething() { . . . }
}
Ktoś może powiedzieć, że w wiekszości przypadków wystarczy modyfikator o zasięgu pakietu (stosunkowo łatwy do obejścia) lub klasy zagnieżdźone (niezbyt ładne), ale mam pewien sentyment do friend z C++.
Proxy
Szybki sposób na coś w rodzaju Proxy z Javy w czasie kompilacji:
public class SomeListenersSet extends HashSet implements SomeListener {
@ProxyToCollection(SomeListener, this);
}
this tutaj to oczywiście nie referencja, tylko wskazanie pola (na samego siebie)
.Net i Java
Oczywiście - Korzystanie z klas .Net i Javy daję dużo możliwości i gotową kolekcję klas
javaimport java.util.HashMap;
public class SomeListenersSet extends HashSet implements SomeListener {
@ProxyToCollection(SomeListener, this);
}
Dla zachowania zgodności z tymi VM wszystko co pochodzi z Nil'u jawnie dziedziczy tylko pierwszą klasę (reszta wygląda tak, jakby były to metody zadeklarowane w klasie) i implementuje tylko jawnie zadeklarowane interfejsy. Oczywiście jeśli jest to wymaganę to użyta zostanie inna klasa (musi być jawnie sprecyzowane - albo w sygnaturze metody wywyoływanej, albo w wywołaniu):
public class Test extends Abc, Def { . . . }
//Java
public class JavaClass {
public void doSomething(Abc abc);
public void doSomething(Xyz xyz);
public void doSomething(Def def);
}
JavaClass javaObject = new JavaClass();
Test test = new Test();
javaObject.doSomething(test); //public void doSomething(Abc abc);
javaObject.doSomething((Xyz)test); //public void doSomething(Xyz xyz);
javaObject.doSomething((Def)test); //public void doSomething(Def def);
Typy generyczne
Bez komentarza
Podsumowanie
Nie mówię, że ten język jest możliwy, ale pomarzyć można...