вопрос о виртуальных методах в Java - PullRequest
3 голосов
/ 26 августа 2010

Проще говоря: я хочу, чтобы следующий код печатал «sub»:

Element e = new SubElement();
print(e);
... 

private static void print(Element e) {
    System.out.println("e");
}

private static void print(SubElement e) {
    System.out.println("sub");
}

, и я не хочу менять печать (Элемент e).поэтому ничего подобного

private static void print(Element e) {
    if (e instanceof SubElement) {
        print((SubElement) e);
    } else {
        System.out.println("e");
    }
}

я бы не хотел:

print(e.getClass().cast(e));

, чтобы автоматически привести его к реальному подклассу и заставить систему войти в печать (SubElement e).это как-то возможно?

Ответы [ 5 ]

7 голосов
/ 26 августа 2010

Перегруженный метод, который запускается, выбирается во время компиляции, поэтому выбирается версия Element, а не версия SubElement.Что может показаться более логичным, если бы элемент или подкласс содержал данные, которые должны быть напечатаны.

class Element {

    public String getName() {
        return "e";
    }
}

class SubElement extends Element {
    public String getName() {
        return "sub";
    }
}

, а затем в методе печати:

private static void print(Element e) {
    System.out.println(e.getName());
}

Будет ли это происходитьсмысл будет зависеть от того, чем на самом деле является класс Element и что представляют печатные данные.

1 голос
/ 26 августа 2010

print() должен стать методом экземпляра Element, действительно.В противном случае вы пытаетесь имитировать полиморфизм трудно.Если вы хотите сделать это, вы не можете избежать ряда if операторов отображений из Class для функциональных объектов.Зачем?

1 голос
/ 26 августа 2010

Да.Вы можете использовать Шаблон посетителя .Однако он подходит для стабильных четко определенных иерархий, поскольку для интерфейса Visitor, который необходимо определить, требуется метод для каждого типа.

interface ElementVisitor {
   visit(Element e);
   visit(SubElement se);
}

class ElementerPrinter implements ElementVisitor {
   visit(Element e) { System.out.println("e"); }
   visit(SubElement e) { System.out.println("sub"); }
}

class Element {
  // here's the trick, Element knows that this is Element
  // and childs have to implement it!
  // if the parent-most class is an interface it force you to implement!
  accept(ElementVisitor visitor) { visitor.visit(this); } 
}

class SubElement {
  // here's the trick, Element knows that this is SubElement
  accept(ElementVisitor visitor) { visitor.visit(this); }
}
0 голосов
/ 26 августа 2010

Я бы выбрал другой подход.Либо

  1. используйте полиморфизм, предложенный другими, расширьте Element, чтобы добавить метод print () (который может быть перезаписан подклассами), либо
  2. определите интерфейс помощника и используйте комбинациюстратегии и фабричного образца:

Базовый класс

 public class Element{}

Производный класс

 public class SubElement extends Element{}

Помощник Интерфейс для печати элементов

public interface PrintHelper{
    void print(Element element);
}

Фабрика, чтобы получить лучший PrintHelper для данного элемента

public class PrintHelperFactory{

    private final Map<Class<? extends Element>, PrintHelper> registeredHelpers =
        new HashMap<Class<? extends Element>, PrintHelper>();

    // Register a PrintHelper for a given Element class.
    public void registerHelper(final Class<? extends Element> clazz,
      final PrintHelper helper){
        this.registeredHelpers.put(clazz, helper);
    }

    // Get the most specific PrintHelper for a given Element.
    public PrintHelper getHelperForElement(final Element element){
        Class<? extends Element> clazz = element.getClass();
        while(!Object.class.equals(clazz)){
            if(this.registeredHelpers.containsKey(clazz)){
                return this.registeredHelpers.get(clazz);
            }
            clazz = (Class<? extends Element>) clazz.getSuperclass();
        }
        return null;
    }

}

Тестовый класс клиента, запустить как Java-приложение

public class Main{

    public static void main(final String[] args){

        final PrintHelperFactory factory = new PrintHelperFactory();
        factory.registerHelper(Element.class, new PrintHelper(){
            @Override
            public void print(final Element element){
                System.out.println("Element");
            }
        });
        factory.registerHelper(SubElement.class, new PrintHelper(){
            @Override
            public void print(final Element element){
                System.out.println("Sub Element");
            }
        });

        // test it with an Element  
        final Element elem = new Element();
        factory.getHelperForElement(elem).print(elem);

        // test it with a sub class
        final Element sub = new SubElement();
        factory.getHelperForElement(sub).print(sub);

    }

}

Вывод

Element
Sub Element
0 голосов
/ 26 августа 2010

Можете ли вы перенести разницу в поведении в классы элементов?

Element e = new SubElement();
print(e);
... 

private static void print(Element e) {
    System.out.println(e.getMessageToPrint());
}

// no longer needed
//
//private static void print(SubElement e) {
//    System.out.println("sub");
//}

Таким образом, SubElement может переопределить метод getMessageToPrint().

Или еще лучше:

Element e = new SubElement();
e.print();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...