2013-08-09 3 views
8

Я хотел бы выполнить несколько операций на основе типа объекта и без использования instanceof. Сначала я думал о перегрузке методов, основанных на типах (как показано ниже), и думал, что, возможно, Java будет выбирать метод соответствующим образом (на основе самого конкретного класса объекта).Сделать Java наиболее конкретным при выборе метода среди перегруженных методов

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 { 

} 

Выход:

Object 
Object 
Object 
Object 

Однако выход я после:

A 
B 
C 
Object 
  • Есть ли способ сделать Java выбрать метод, основанный на наиболее конкретный тип объекта параметра?
  • Если нет, то какие альтернативы я могу смотреть на такой функциональности, без помощи instanceof
  • Я был на самом деле пытается имитировать шаблон посетителя однако, кажется, что делает шаблон посетитель работы из-за двойной отправки , что делает параметр «принят» в правильном типе во время вызова функции, в частности, visitor.accept(this) в class A вызывает вызов функции visitor.accept(A o).
  • Я честно против instanceof, потому что я читал, используя это плохая практика; в этом случае, будет ли это еще плохая практика?
+0

Разве ваш 'C' класс не расширяет' B' или 'A'? –

+0

@RohitJain Nope. Предполагается, что C - это еще один класс. – Chad

+1

Тогда я боюсь, что вы не сможете этого сделать без проверки «instanceof» и соответствующего кастинга. Тем не менее, позвольте мне еще раз взглянуть на ваш код. –

ответ

4

Этот ответ считает, что изменение заданных классов и их взаимосвязь не является вариантом.

Есть ли способ заставить Java выбрать метод, основанный на самом конкретном типе объекта параметра?

Компилятор Java не может использовать это для вас, потому что, возможно, это не то, что вы хотите (в вашем случае это то, что вы хотите, но, возможно, другие люди не хотят этого поведения, и они не будут решением для них). Поскольку вы передаете ссылку Object на номер print(), компилятор позвонит print(Object).

Если нет, то какие альтернативы я могу смотреть на такой функциональности, без помощи InstanceOf

Вы можете обернуть ваш Object с его типа в класс-оболочку, например:

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

Таким образом, вы можете безопасно отливать тип Object без использования instanceof. Хотя это ИМХО гораздо сложнее, чем просто с помощью instanceof, а на самом деле он создает только свои собственные instanceof ...

Я честно против InstanceOf потому, что я прочитал, используя это плохо практика; в этом случае, будет ли это еще плохая практика?

В программировании ничего не было плохой практикой. Все зависит от ситуации. В этой ситуации я думаю, вы можете использовать instanceof и делать небезопасное литье, потому что это требует дизайн. И вообще, если в языке существует оператор instanceof, это потому, что он используется. Если instanceof был всегда Плохая практика, она не существовала бы как часть языка. См. this и this.

Я был на самом деле пытаюсь имитировать шаблон посетителя однако кажется, то, что делает шаблон посетителя работа из-за двойную отправки, что делает параметр быть «принято» быть в правильном типа во время вызова функции , в частности, guest.accept (this) в классе A вызывает вызов функции visitor.accept (A o).

Проверьте мою вторую ссылку на недостатки, используя шаблон посетителя.

3

Метод Перегрузки является время компиляции полиморфизм, в вашем for цикле, вы объявили o как Object и, следовательно, всегда печати (Object о) будет называться.

Решение: Использование динамического полиморфизма:

class A{ 

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

class B{ 

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

и цикл

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

Этот ответ кажется очень точным для того, что мне нужно. Однако существует ли способ реализовать это ** без изменения ** указанных классов? – Chad

+0

Не боюсь, вам нужно вызвать метод 'print()' в объекте 'o' и не передавать его в общий метод. –

+0

@ Если вам необходимо изменить код в указанных классах. – Ankit

11

Ну мой ответ был бы сделать классы A, B и C реализовать общий интерфейс.

И каждый из них может иметь свои конкретные реализации этого интерфейса.

Таким образом, вы можете вызвать тот же метод для всех объектов (избегая таким образом перегруженные методы), а также обеспечить пользовательские функции в зависимости от типа объекта (т.е. класса, из которого он был реализованным) ,

+0

Это хороший ответ в целом, но он включает в себя изменение определений A, B и C. OP упоминает в комментарии, что это не вариант для него. – overthink

+2

@overthink, в этом случае расширьте все три класса с помощью оберток, реализующих общий интерфейс, и используйте те обертки (/ этот интерфейс) вместо объектов A, B, C. Опять же, не вариант, если A, B и/или C являются конечными классами, но жизнеспособными в противном случае. – LJ2

+0

@ LJ2 Да, хорошее предложение. Определенно вариант. – 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(); 
       } 

     } 


    } 

Выход: - Б С Базовая

+0

Хммм вы так сильно изменили свой код, что теперь он не имеет никакого отношения к исходному вопросу ... – m0skit0

+0

Я просто дал подход. Но вы правы, вероятно, это не лучший подход. – Ankit

2

Рассмотрим посетителей шаблон дизайна http://en.wikipedia.org/wiki/Visitor_pattern. Хотя это потребует изменений в A, B, C, иначе нет другого пути.

+0

Это был образец, на который я смотрел (как указано в сообщении), но я стараюсь ** не изменять классы. – Chad

+0

, то вам придется использовать instainceof :) –

+0

@EvgeniyDorofeev Не насильственно. См. Мой ответ для (плохой) альтернативы. – m0skit0

0

Что выскочит из моей памяти без использования InstanceOf сейчас:

  • Используйте общий интерфейс для всех из них имеют общий метод:

Код:

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(); 
}   
  • Использование Enum, объявляющего абстрактный метод tha будет осуществляется каждого типа у вас есть:

Код:

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

После прочтения выше ответа

Одно решение, которое я могу предложить будет использовать проверить тип объекта с помощью o.getClass и продолжить

Однако многие лучшие решения опубликованы. Я полностью согласен с тем, что @krisna kishor shetty сказал

+2

Проверка 'o.getClass()' такая же, как с использованием 'instanceof', но медленнее ... – m0skit0

0

С моей точки зрения, дело не в том, пользуется ли instanceof, либо нет. Вопрос заключается в том, как минимизировать влияние этой ситуации. Поэтому я бы порекомендовал адаптер экземпляра здесь, если есть несколько операций для решения. Таким образом, вы можете по крайней мере, избежать распространения этой сложности всей системы:

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"); 
    } 
} 

Затем цикл:

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

Конечно наш адаптер будет иметь ссылку на обернутого объекта, как следует из названия метода. Таким образом, вы получаете модель кодирования, близкую к прямой модификации этих классов.

1

Я бы просто использовал instanceof здесь. Это просто и понятно.

Не похоже, что существует правило не использовать instanceof. Нет жестких правил :) Многое использование instanceof может быть признаком того, что вы можете изменить ситуацию, чтобы заставить компилятор сделать больше работы для вас. Независимо от того, стоит ли это делать, нужно смотреть в каждом случае. В вашем случае вы упоминаете, что не можете изменить классы, о которых идет речь, поэтому это даже не вариант.

Смежные вопросы