- 1
- 2
-
george
Wlasnie zaliczylem niezle zdziwko. Zawsze mi sie zdawalo ze ponizszy kod;
public static void main(String[] args) {
// TODO Auto-generated method stub
T1_IMPL t1=new Implementor_1();
doSomething(t1);
}
public static void doSomething(T1_IMPL t1)
{
System.out.println("CALL TO T1");
}
public static void doSomething(Implementor_1 t1)
{
System.out.println("CALL TO Implementor_1");
}
public static void doSomething(Implementor_2 t1)
{
System.out.println("CALL TO Implementor_2");
}
}
powinien dac w rezultacie "CALL TO Implementor_1" (Implemneto_X extends T1_IMPL).
Czemy tak sie nie dzieje? Cos gdzies mi sie przewinelo ze kompilator musi podczas kompilacji zapisac info ktore cialo wywolac.
Jednak cos we mnie walczy - pamietam takie stwierdzenie : JVM wykrywa prawdziwy typ obiektu i zaleznie od niego wywoluje odpowiednia metode, typ obiektu nie jest zalezny ot typu referencji - czy pomieszalo mi sie cos tutaj i powyzszy urywek odnosi sie do polimorfizmu? -
Koziołek [brat Javowiec]
Wszytko jest dobrze :)
t1 jest typu T1_IMPL i jako taki jest rozumiany przez JVM. To co w rzeczywistości kryje się za referencją jest zmniej ważne niż to w jaki sposób została zdefiniowana referencja. Kolejnym ważnym elementem jest to co opisałem tu:
http://koziolekweb.blogspot.com/200...
już kompilator ma pewne mechanizmy promowania i wartościowania obiektów. W tym przypadku masz do czynienia nie z nullem ale z instancją interfejsu. -
Koziołek [brat Javowiec]
-
boska renia
Odpowiedź na powyższe wątpliwości jest oczywista.
Jeśli mamy dwie metody:
void sayHello(A a);
void sayHello(B b);
gdzie B extends A (niezależnie, czy jest to interfejs, czy klasa);
to wywołanie
A a = new B();/ / B jest tu klasą
sayHello(a);
jest równoznaczne z rzutowaniem:
B b = new B();
sayHello((A) b);/ / w ten sposób wybieramy metodę o którą nam chodzi
natomiast wywołując:
sayHello(null);
musimy podać o którą metodę nam chodzi; tak jak powyżej: sayHello((A) null) albo sayHello((B) null), inaczej kompilator nie będzie wiedział którą metodę ma odpalić.
Pozdrawiam
-
-
boska renia
...czyli nie o polimorfizm tu chodzi, tylko o zasady wybierania metody przeciążąnej -- inny to rozdział w książce;) -
Koziołek [brat Javowiec]
>boska renia napisał
>Odpowiedź na powyższe wątpliwości jest oczywista.
>
>Jeśli mamy dwie metody:
>
>void sayHello(A a);
>
>void sayHello(B b);
>
>gdzie B extends A (niezależnie, czy jest to interfejs, czy
>klasa);
>
>to wywołanie
>
>A a = new B();/ / B jest tu klasą
>sayHello(a);
>
>jest równoznaczne z rzutowaniem:
>
>B b = new B();
>sayHello((A) b);/ / w ten sposób wybieramy metodę o
>którą nam chodzi
>
>
>natomiast wywołując:
>
>sayHello(null);
>
>musimy podać o którą metodę nam chodzi; tak jak
>powyżej: sayHello((A) null) albo sayHello((B) null),
>inaczej kompilator nie będzie wiedział którą metodę ma
>odpalić.
>
>Pozdrawiam
>
Poprawka. kompilator będzie doskonale wiedział którą metodę wybrać jeżeli podamy mu null. http://koziolekweb.blogspot.com/200...
W kompilatorze są zaszyte zasady rzutowania i ważności metod. -
boska renia
> Poprawka. kompilator będzie doskonale wiedział którą metodę wybrać jeżeli podamy mu null. http://koziolekweb.blogspot.com/200...
> W kompilatorze są zaszyte zasady rzutowania i ważności metod.
Zdecydowanie oponuję, mój kompilator tego nie przełyka:
void sayHello(A a){
System.out.println("say A: "+a.hello());
}
void sayHello(B b){
System.out.println("say B: "+b.hello());
}
void sayHello(String s){
System.out.println("Say string: "+S);
}
public void hello() {
sayHello(null);/ / Błąd kompilatora: The method sayHello() is ambiguous for type TestSayHello
}
// gdyby nie bylo sayHello(String), to kompilator wybierze zwykle metodę z klasą bazową, tzn. sayHello(A) -
george
Nie wie, poniewaz przekazujesz null "z palce" jesli masz referencje nullowa to wywola zaleznie od typu tej referencji.
Problem u mnie byl taki :
Interface Super
{
}
Interface Client extends Super
{
}
Interface Server extends Super
{
}
W jakiejs tam klase mialem trzy metody:
doSomething(Super o);
doSomething(Server s);
doSomething(Client c);
Przy
Super s=.... - instancja obiektu impl Server,Client
doSomething(s);
JVM wywoluje doSomething(Super); cyzzby w przypadku interfejsow bylo troche inaczej z wyborem metody?
public static void main(String[] args) {
// TODO Auto-generated method stub
Super s=new ClientImpl();
doSomething(s);
}
public static void doSomething(Super s)
{
System.out.println("SSSS");
}
public static void doSomething(Client c)
{
System.out.println("CCCC");
}
public static void doSomething(Server s)
{
System.out.println("SSSS");
}
da SSSS, czyli kicha :) -
boska renia
dlaczego kicha? z interfejsami jest dokładnie tak samo, wywołana zostaje metoda, zgodnie z zadeklarowanym typem referencji (Super s) -
Lilianne E. Blaze
W skrocie, to jest cos czego nie nalezy robic jesli nie ma sie bardzo dobrego powodu zeby to zrobic. Oczywiscie to moje subiektywne zdanie.
Dlaczego - bo nie masz gwarancji ze za tydzien ktos tego nie przerzuci na przyklad na jakis jezyk skryptowy gdzie reguly wybierania beda inne. Albo po prostu ze nastepna osoba ktora bedzie pracowac przy tym kodzie bedzie wiedziala czego sie spodziewac.
Wiekszosc sytuacji gdzie cos takiego moze miec zastosowanie lepiej rozwiazac przez jedna metode z if( x instanceof X). Moze mniej eleganckie, ale bardziej czytelne. -
steelheart
>Lilianne E. Blaze napisała:
>W skrocie, to jest cos czego nie nalezy robic jesli nie ma
>sie bardzo dobrego powodu zeby to zrobic. Oczywiscie to moje
>subiektywne zdanie.
imho programowanie zorientowane obiektowo jest wystarczajacym powodem...
>Dlaczego - bo nie masz gwarancji ze za tydzien ktos tego nie
>przerzuci na przyklad na jakis jezyk skryptowy gdzie reguly
>wybierania beda inne.
no tak, bo jezyki w projektach sie zmienia od tak i robi sie copy,paste kodu, poczym regexpami zmienia sie kod z np c# na ruby... litości...
> Albo po prostu ze nastepna osoba ktora
>bedzie pracowac przy tym kodzie bedzie wiedziala czego sie
>spodziewac.
jesli ta 'nastepna osoba' nie rozumie Javy/OOP to niech zmieni prace
>Wiekszosc sytuacji gdzie cos takiego moze miec zastosowanie
>lepiej rozwiazac przez jedna metode z if( x instanceof X).
>Moze mniej eleganckie, ale bardziej czytelne.
weeee !! i niech ta metoda bedzie statyczna ! (tak jak wszystkie pozostale w systemie :P ) -
Lilianne E. Blaze
>steelheart napisał
>>Lilianne E. Blaze napisała:
>>W skrocie, to jest cos czego nie nalezy robic jesli nie ma
>>sie bardzo dobrego powodu zeby to zrobic. Oczywiscie to
>moje
>>subiektywne zdanie.
>
>imho programowanie zorientowane obiektowo jest
>wystarczajacym powodem...
OOP to narzedzie, nie religia.
Przyklad od poczatku jest 'specyficzny', bo jesli trzymac sie na sztywno regul OOP to wybor logiki powinien byc w T1_IMPL, nie w klasie uzywajacej T1_IMPL.
W 3 przypadkach na 4 jesli potrzebujesz dwoch metod doSomething(X) i doSomething(Y) gdzie X extends Y to jest to wynik nieprzemyslanego api, i nawet wtedy ma to sens przewaznie tylko wtedy kiedy jedna wersja robi jakis sanity check i wywoluje druga.
>>Dlaczego - bo nie masz gwarancji ze za tydzien ktos tego
>nie
>>przerzuci na przyklad na jakis jezyk skryptowy gdzie
>reguly
>>wybierania beda inne.
>
>no tak, bo jezyki w projektach sie zmienia od tak i robi sie
Najprostrzy przyklad - przeniesienie czesci kodu inicjalizacyjnego z Javy do JavaScriptu. Nigdy nie widziales?
>copy,paste kodu, poczym regexpami zmienia sie kod z np c# na
>ruby... litości...
>
>
>> Albo po prostu ze nastepna osoba ktora
>>bedzie pracowac przy tym kodzie bedzie wiedziala czego sie
>>spodziewac.
>
>jesli ta 'nastepna osoba' nie rozumie Javy/OOP to niech
>zmieni prace
Jesli ta nastepna osoba ma lepsze gadane od Ciebie albo jest kuzynem szefa to jak myslisz, kto oberwie?
>
>>Wiekszosc sytuacji gdzie cos takiego moze miec
>zastosowanie
>>lepiej rozwiazac przez jedna metode z if( x instanceof X).
>>Moze mniej eleganckie, ale bardziej czytelne.
>
>weeee !! i niech ta metoda bedzie statyczna ! (tak jak
>wszystkie pozostale w systemie :P )
Jesli jestes z obozu wszystko-wlacznie-z-boolean-i- int-powinno-byc-obiektem to dalsza dyskusja nie ma sensu, nie dogadamy sie. Ja wole zarabiac niz ewangelizowac, a Java podoba mi sie miedzy innymi dlatego ze nie jest obiektowa-dla-obiektowosci, tylko dla wygody programisty. -
george
oj i tutaj troche sie nie zgodze. Wlasnie to wedlug mojego skromnego zdania powinno dzialac "jak mi sie uwidzialo" (teraz smiga na instanceof ..; [ )
1. Kod napewno nie bedzie konwertowany, srodowisko czysto javowe
2. RTTI i jeszcze cos dziala przy klasach, czemu nie przy interfejsach? (szczegolnie ze implementujace klasy dziedzicza z jednej ktora implementuje "SuperINterfejs").
3. Nie przemyslanego? nie rozumiem tego zupelnie. Jezli masz kolekcje w ktorej trzymasz obiekty ktore sa typu SuperInterfejs, ktory zapewnia minimalna uzytkowosc, ktora pozwala na interakcje z tymi obiektami w jakims systemie, niezaleznie od ich prawdziwego typu? Czy nie prosciej zdefiniowac sobie dla kazdego obiektu doSomething(XXX) i jedna dla SuperInterfejsu gdzie mozna zrobic
cos bardziej magicznego i nie zasmiecac kodu istanceof i masa if'ow ?
W tym konkretnym przypadku
SuperWrapperInterface
{
/ /....
}
ClientTransactionWrapper extends SuperWrapperInterface
{
/ /.....
}
ServerTransactionWrapper extends SuperWrapperInterface
{
/ /....
}
HashMap<ActivityHandle,SuperWr apperInterface> ....
4. Nie mieszajmy kodu i polityki :) Tutaj to przypadek wynikajacy czysto z mojej sklerozy, bylem pewien, ze TO powinno tak dzialac w javie.
5. Nie rozumiem tego wyboru logiki w T1_IMPl :) -
Lilianne E. Blaze
Ok, przyklad. Mamy klasy Glowna, Figura, Kolo, Kwadrat.
Scenariusz 1:
Glowna{
void rysuj(Kolo kolo) { ... }
void rysuj(Figura figura} { throw new RuntimeException("WTF?"); }
void rysuj(Kwadrat kwadrat) { }
}
Od tego sie zaczelo, tak? Moze i to jest ladnie slicznie obiektowe, ale czy naprawde o to chodzi?
Scenariusz 2:
Glowna {
void rysuj(Figura figura) { figura.rysuj(); }
}
Chyba zdecydowanie czytelniejsze? O to chodzilo z tym wyborem logiki w T1_IMPL.
Scenariusz 3:
Glowna {
void rysuj(Figura figura) {
if( figura instanceof Kolo ) {
// rysuj kolo
} else if( figura instanceof Kwadrat ) {
// rysuj kwadrat
} else {
throw new RuntimeException("WTF?");
}
}
}
A jesli nie mozemy jak w #2, to to jest jednak czytelniejsze od #1, mimo ze mniej 'obiektowe'. I latwiej dodac wspolna obsluge bledow, sanity checks, i inne takie. Wreszcie jest to mniej podatne na problemy copy-paste.
I bardzo ladnie prosze bez tekstow ze instanceof i if nie sa wystarczajaco obiektowe. Ostatecznie i tak wszystko idzie w zera i jedynki. -
bartkiller
>Lilianne E. Blaze napisała:
>Scenariusz 3:
>Glowna {
>void rysuj(Figura figura) {
>if( figura instanceof Kolo ) {
>// rysuj kolo
>} else if( figura instanceof Kwadrat ) {
>// rysuj kwadrat
>} else {
>throw new RuntimeException("WTF?");
>}
>}
>}
Jeśli już miałbym takie gówno napisać to szczerze mówiąc wolałbym nie odwalać za kompilator roboty. Po to jest przeciążanie metod, żeby takich rzeczy nie musieć pisać. -
george
>Lilianne E. Blaze napisała:
>Ok, przyklad. Mamy klasy Glowna, Figura, Kolo, Kwadrat.
>
>Scenariusz 1:
>Glowna{
>void rysuj(Kolo kolo) { ... }
>void rysuj(Figura figura} { throw new
>RuntimeException("WTF?"); }
>void rysuj(Kwadrat kwadrat) { }
>}
>
>Od tego sie zaczelo, tak? Moze i to jest ladnie slicznie
>obiektowe, ale czy naprawde o to chodzi?
>
>Scenariusz 2:
>Glowna {
>void rysuj(Figura figura) { figura.rysuj(); }
>}
>
>Chyba zdecydowanie czytelniejsze? O to chodzilo z tym
>wyborem logiki w T1_IMPL.
>
Problem w tym, ze w tym przypadku obiekty Figura musza buc swiadome klasy Glowna, calej jej logiki i srodowiska. Moze to wydaje sie dobrym pomyslem, ale gdy mamy do czynienia z roznymi konetenerami i sorodowiskiem zdazeniowym, to wszystko moze latwo pojsc w diably. Dodatkowo jesli potrzebujemy dostepu w "rysuj" do prywatnego stanu instancji klasy Glowna, zrobimy z niej przechodni burdel.
>Scenariusz 3:
>Glowna {
>void rysuj(Figura figura) {
>if( figura instanceof Kolo ) {
>// rysuj kolo
>} else if( figura instanceof Kwadrat ) {
>// rysuj kwadrat
>} else {
>throw new RuntimeException("WTF?");
>}
>}
>}
>
>A jesli nie mozemy jak w #2, to to jest jednak czytelniejsze
>od #1, mimo ze mniej 'obiektowe'. I latwiej dodac wspolna
>obsluge bledow, sanity checks, i inne takie. Wreszcie jest
>to mniej podatne na problemy copy-paste.
Istnieje cos takiego co kompoilator i JVM uzywaja za nas - RTTI :), po to obiekty maja dodatkowy blok pamieci zeby mozna bylo zidentyfikowac ich prawdziwy typ - wlasnie ten blok jest uzywany przy operacji instanceof. Owszem, przy bardziej zlozonych scenariuszach, drzewach dziedziczenia moze byc to mniej wydajne, ale to uz zalezy jak sie "to" zapisze.
>I bardzo ladnie prosze bez tekstow ze instanceof i if nie sa
>wystarczajaco obiektowe. Ostatecznie i tak wszystko idzie w
>zera i jedynki.
-
bartkiller
>george napisał
>Istnieje cos takiego co kompoilator i JVM uzywaja za nas -
>RTTI :),
ale tu rozmawiamy o problemie, który da się rozwiązać statycznie i RTTI nie ma nic do tego. Jakby zastosować wizy(ta)tora to w grę wchodziłby co najwyżej polimorfizm -
boska renia
Jak dla mnie, to najbardziej obiektowym jest scenariusz 2:
>Scenariusz 2:
>Glowna {
>void rysuj(Figura figura) { figura.rysuj(); }
>}
>
Przeciążanie samo w sobie ma niewiele (nic) wspólnego z obiektowością, jest przeniesieniem konstrukcji z C/C++. Ogólnie rzecz biorąc, staram się minimalizować konieczność używania przeciążania jedynie do przypadków prostych i zupełnie oczywistych, a w innych stosować scenariusz 4 (o tym niżej).
Uwaga! Przeciążanie jest szczególnie niepożądane w interfejsach logiki biznesowej Java EE. Pojawienie się takich metod może spowodować problemy z bindowaniem do technologii webserwisowych (i nie tylko takich). WS-I Basic Profile wprost zakazuje stosowania przeciążania (WS-I Basic Profile 1.0 Final Material, punkt 5.4.3).
Poza wspomnianymi przez LB scenariuszami jest jeszcze jeden:
Scenariusz 4:
Glowna{
void rysujKolo(Kolo kolo) { ... }
void rysujFigure(Figura figura} { throw new RuntimeException("WTF?"); }
void rysujKwadrat(Kwadrat kwadrat) { }
}
I poza scenariuszem 2 ten właśnie jest najrozsądniejszy, bowiem nie pozostawia wątpliwości co zostaje wywołane. -
boska renia
Natomiast wspomniany scenariusz:
>
>Interface Super
>{
>}
>
>Interface Client extends Super
>{
>}
>Interface Server extends Super
>{
>}
>
>
>
>W jakiejs tam klase mialem trzy metody:
>doSomething(Super o);
>doSomething(Server s);
>doSomething(Client c);
jest wprost błędny, jeśli ma być narzędziem dynamicznego wyboru metody w czasie wywołania.
InnaKlasa {
void zrobCos(JakasKlasa jakisObiekt, Super s){
jakisObiekt.doSomething(s);
}
}
Niezależnie od tego, jakim obiektem będzie jakasObiekt, to zawsze zostanie wywołana metoda void doSomething(Super s}! Do dynamicznego wyboru metody konieczne jest zastosowanie albo instanceof, albo scenariusza 2 (czy podobnej odmiany wzorca visitator).
Przeciążanie, mimo że daje taką iluzję, nie jest narzędziem dynamicznego wyboru metod w trakcie wywołania, jest jak ktoś powyżej wspomniał, mechanizmem statycznym realizowanym przez kompilator, a nie JVM!
Ta iluzoryczność jest kolejnym argumentem, dlaczego nie warto korzystać z przeciążania (na rzecz scenariusza 4 albo 2). -
boska renia
>bartkiller napisał
>>Lilianne E. Blaze napisała:
>>Scenariusz 3:
>>Glowna {
>>void rysuj(Figura figura) {
>>if( figura instanceof Kolo ) {
>>// rysuj kolo
>>} else if( figura instanceof Kwadrat ) {
>>// rysuj kwadrat
>>} else {
>>throw new RuntimeException("WTF?");
>>}
>>}
>>}
>
>Jeśli już miałbym takie gówno napisać to szczerze
>mówiąc wolałbym nie odwalać za kompilator roboty. Po to
>jest przeciążanie metod, żeby takich rzeczy nie musieć
>pisać.
Dlatego właśnie komentarz jest błędny - to nie kompilator wywołuje instanceof, tylko JVM. Tego, co zaproponowała LB nie da się zrealizować za pomocą przeciążania metod!
- 1
- 2
- Przeglądaj grona w kategorii Internet i Komputery
- Przeglądaj grona w okolicy Warszawa
- Załóż własne grono tematyczne
- Zostań moderatorem
Podobne Tematy
|
|
Wszystko co związane z programowaniem w Java (J2EE, JSP, JDBC, itd) test
Miejsca grona (1)
-
Kino Luna ul. Marszałkowska, Warszawa
www.kinoluna.pl kino.luna@maxfilm.com.pl 22 621 78 28
- Dodaj miejsce

