2013-08-09 16 views
8

Chciałbym wykonać kilka operacji na podstawie typu obiektu i bez użycia instanceof. Najpierw myślałem o przeciążeniu metod opartych na typach (jak widać poniżej) i pomyślałem, że być może Java wybrałaby metodę odpowiednio (w oparciu o najbardziej specyficzną klasę obiektu). tamUstaw Java jako najbardziej specyficzny typ przy wyborze metody spośród przeciążonych metod.

A 
B 
C 
Object 
  • jest sposobem, aby wybrać metodę Java w oparciu o najbardziej:

    import java.util.ArrayList; 
    import java.util.List; 
    
    
    public class TestA { 
    
        public static void main(String[] args) 
        { 
         List<Object> list = new ArrayList(); 
         list.add(new A()); 
         list.add(new B()); 
         list.add(new C()); 
         list.add(new Object()); 
    
         TestA tester = new TestA(); 
    
         for(Object o: list) 
         { 
          tester.print(o); 
         } 
        } 
    
        private void print(A o) 
        { 
         System.out.println("A"); 
        } 
    
        private void print(B o) 
        { 
         System.out.println("B"); 
        } 
    
        private void print(C o) 
        { 
         System.out.println("C"); 
        } 
    
        private void print(Object o) 
        { 
         System.out.println("Object"); 
        } 
    } 
    
    class A { 
    
    } 
    
    class B extends A { 
    
    } 
    
    class C { 
    
    } 
    

    Wyjście jest:

    Object 
    Object 
    Object 
    Object 
    

    Jednak wyjście Jestem Po określony typ obiektu parametru?

  • Jeśli nie, jakie alternatywy mogę patrzeć na taką funkcjonalność, bez pomocy instanceof
  • I faktycznie próbuje symulować wzór odwiedzających jednak wydaje się, co sprawia, że ​​praca odwiedzający jest ze względu na podwójną wysyłką , który sprawia, że ​​parametr "akceptowany" jest we właściwym typie podczas wywołania funkcji, w szczególności visitor.accept(this) w class A powoduje wywołanie funkcji visitor.accept(A o).
  • Szczerze mówiąc, nie zgadzam się z instanceof, ponieważ przeczytałem, że jest to zła praktyka; w takim przypadku czy nadal byłaby to zła praktyka?
+0

Czy twoja klasa 'C' nie rozszerza' B' lub 'A'? –

+0

@RohitJain Nope. Chodzi o to, że C to zupełnie inna klasa. – Chad

+1

Wtedy obawiam się, że nie możesz tego zrobić bez sprawdzenia "instanceof" i odpowiedniego rzucania. Mimo to pozwól mi jeszcze raz spojrzeć na twój kod. –

Odpowiedz

4

W tej odpowiedzi uważa się, że modyfikowanie danych zajęć i ich relacji nie jest możliwe.

Czy istnieje sposób, aby Java wybrać metodę opartą na najbardziej konkretnego typu obiektu parametru?

Java kompilator nie może rzucić to dla ciebie, bo może to nie to, co chcesz (w Twoim przypadku to, co chcesz, ale może inni ludzie nie chcą tego zachowania i byliby żadne rozwiązanie dla nich). Ponieważ podajemy Object odniesienie do print(), kompilator wywoła print(Object).

Jeśli nie, jakie alternatywy mogę patrzeć na taką funkcjonalność, bez pomocy instanceof

Można owinąć Object z tego typu w klasie otoki, np

public class ObjectWrapper { 
    private final Object object; 
    private final int type; 
} 

W ten sposób można bezpiecznie rzucić na typ Object bez korzystania z instanceof. Mimo to IMHO jest bardziej skomplikowane niż po prostu za pomocą instanceof, a właściwie to tylko tworzy własne instanceof ...

Jestem szczerze przed instancją, bo czytałem użyciu jest zły praktyka; w takim przypadku czy nadal byłaby to zła praktyka?

W programowaniu, nic nie jest zawsze złą praktyką. Wszystko zależy od sytuacji. W tej sytuacji myślę, że możesz użyć instanceof i wykonać niebezpieczne rzucanie, ponieważ wymaga tego projekt. A zresztą, jeśli operator istnieje w danym języku, to dlatego, że jest używany. Jeśli instanceof byłaby złą praktyką, nie istniałaby jako część języka. Zobacz this i this.

I faktycznie próbuje symulować odwiedzający jednak zdaje się, co sprawia, że ​​praca odwiedzający z powodu podwójnego wysyłki, który sprawia, że ​​parametr jest „akceptowana” być w prawidłowej typu podczas wywołania funkcji , szczególnie visitor.accept (this) w klasie A powoduje wywołanie funkcji visitor.accept (A o).

Sprawdź mój drugi link, aby dowiedzieć się więcej o korzystaniu z wzoru gościa.

3

Metoda Przeciążenie jest polimorfizm czas kompilacji, w pętli for, zostały zadeklarowane jako Objecto i stąd zawsze wydrukować (Object o) zostanie wywołana.

Rozwiązanie: używać dynamicznych Polimorfizm:

class A{ 

    void print(){ 
    System.out.println('in A'); 
    } 
} 

class B{ 

    void print(){ 
    System.out.println('in B'); 
    } 
} 

i pętli for

for(Object o: list){ 
    o.print(); 
} 
+0

Ta odpowiedź wydaje się bardzo wskazująca na to, czego szukam. Czy istnieje jednak sposób na wdrożenie tego ** bez modyfikowania ** określonych klas? – Chad

+0

Obawiam się, że nie, musisz wywołać metodę 'print()' na obiekcie 'o' i nie przekazywać jej we wspólnej metodzie. –

+0

@Chad you będzie musiał dokonać zmiany kodu w określonych klasach. – Ankit

11

Więc moja odpowiedź byłaby do klasy A, B i C wdrożyć wspólny interfejs.

A następnie każdy z nich może mieć swoje własne implementacje tego interfejsu.

ten sposób można wywołać sam sposób dla wszystkich obiektów (unikając przeciążone metody), a także zapewnić niestandardową funkcjonalność w oparciu o typ obiektu (czyli klasy, z którego została instancja) .

+0

Jest to ogólnie dobra odpowiedź, ale wiąże się ona ze zmianą definicji A, B i C. OP mówi w komentarzu, że nie jest to opcja dla niego. – overthink

+2

@w takim przypadku, rozszerz wszystkie trzy klasy za pomocą wrapperów, które implementują wspólny interfejs, i użyj tych wrapperów (/ tego interfejsu) zamiast obiektów A, B, C. Ponownie, nie jest to opcja, jeśli A, B i/lub C są klasami końcowymi, ale w inny sposób wykonalne. – LJ2

+0

@ LJ2 Tak, dobra sugestia. Zdecydowanie opcja. – overthink

0
package com.ank.says; 
    import java.util.ArrayList; 
    import java.util.List; 

    class Base{ 
     public void print(){ 
      System.out.println("Base"); 
     } 

    } 

    class A extends Base{ 
     @Override 
     public void print() { 
      System.out.println("A"); 
     } 
    } 

    class B extends Base{ 
     @Override 
     public void print(){ 
      System.out.println("B"); 
     } 
    } 

    class C extends Base{ 
     @Override 
     public void print(){ 
      System.out.println("C"); 
     } 
    } 



    public class TestPlayGround { 

     public static void main(String[] args) throws ParseException { 
       List<Base> list = new ArrayList<Base>(); 
       list.add(new A()); 
       list.add(new B()); 
       list.add(new C()); 
       list.add(new Base()); 

       TestPlayGround tester = new TestPlayGround(); 

       for(Base o: list) 
       { 
        o.print(); 
       } 

     } 


    } 

wyjściowa: - B C Baza

+0

Hmmm zmodyfikowałeś jego kod tak bardzo, że nie ma to teraz nic wspólnego z pierwotnym pytaniem ... – m0skit0

+0

Po prostu podałem podejście. Ale masz rację, to prawdopodobnie nie jest najlepsze podejście, aby iść do przodu. – Ankit

2

Rozważmy Visitor wzornictwo http://en.wikipedia.org/wiki/Visitor_pattern. Chociaż będzie to wymagało zmian w A, B, C inaczej nie ma innego sposobu.

+0

To był właściwie wzór, na który patrzyłem (jak określono w poście), ale staram się ** nie ** modyfikować klas. – Chad

+0

następnie będziesz musiał użyć instainceof :) –

+0

@EvgeniyDorofeev Nie siłą. Zobacz moją odpowiedź na (złą) alternatywę. – m0skit0

0

Co wyskoczy z mojego umysłu bez użycia instanceof teraz jest:

  • Użyj wspólny interfejs dla wszystkich z nich mają wspólną metodę:

Kod:

public interface YourInterface { 
    public void doStuff(); 
} 

public class A implements YourInterface { 
    @Override 
    public doStuff() { 
     System.out.print("A"); 
    } 
} 

public class B implements YourInterface { 
@Override 
    public doStuff() { 
     System.out.print("A"); 
    } 
} 

List<YourInterface> list = new ArrayList<YourInterface>(); 
    list.add(new A()); 
    list.add(new B()); 

for(YourInterface e: list) 
{ 
    e.doStuff(); 
}   
  • Użyj Enum deklarując metodę abstrakcyjną, która będzie realizowane przez każdego rodzaju masz:

Kod:

public enum YourEnum { 
    TYPE1 { 
    @Override 
    public void doStuff() { 
     System.out.print("Type 1"); 
    }, 

    TYPE2 { 
    @Override 
    public void doStuff() { 
     System.out.print("Type 2");   
    }; 

    public abstract void doStuff(); 
} 

List<YourEnum> list = new ArrayList<YourEnum>(); 
    list.add(YourEnum.TYPE1); 
    list.add(YourEnum.TYPE2); 

for(YourEnum e: list) 
{ 
    e.doStuff(); 
}   
0

Po przeczytaniu powyżej odpowiedź

Jedno rozwiązanie, które mogę zasugerować byłoby użyć sprawdzić typ obiektu za pomocą o.getClass i continue

Jednak wiele lepszych rozwiązań zostało opublikowanych. Całkowicie zgadzam się z tym, co @krisna kishor shetty powiedział

+2

Sprawdzanie 'o.getClass()' jest takie samo jak użycie 'instanceof', ale wolniej ... – m0skit0

0

Z mojego punktu widzenia to nie jest pytanie, czy użyć instanceof. Pytanie brzmi, jak zminimalizować wpływ uzyskany w tej sytuacji. Dlatego polecam tutaj adapter na wypadek, gdyby istniało kilka operacji. W ten sposób można przynajmniej uniknąć rozprzestrzeniania się, że złożoność całego systemu:

public abstract class Adapter { 
    public abstract void print(); 

    public static Adapter wrap(Object o) { 
    if (o instanceof A) { 
     return new AAdapter(); 
    } 
    if (o instanceof B) { 
     return new BAdapter(); 
    } 
    return new ObjectAdapter(): 
} 

public class AAdapter { 
    public void print() { 
    System.out.printLn("A"); 
    } 
} 

Następnie pętla:

for (Object o: things) { 
    Adapter.wrap(o).print(); 
} 

Oczywiście nasz adapter będzie miała odniesienie do zawiniętego obiektu jak sama nazwa metoda zakłada. W ten sposób otrzymasz model kodowania blisko bezpośredniej modyfikacji tych klas.

1

Po prostu użyłbym tutaj instanceof. To proste i przejrzyste.

Nie jest tak, że istnieje twarda zasada, aby nie używać instanceof. Nie ma twardych zasad :) Używanie instanceof może oznaczać, że możesz zmienić rzeczy, aby kompilator mógł wykonać dla ciebie więcej pracy. To, czy rzeczywiście warto to robić, należy rozpatrywać indywidualnie. W twoim przypadku wspomniałeś, że nie możesz zmienić zajęć, o których mowa, więc nie jest to nawet opcja.