2015-07-31 2 views
4

Я нашел, что не могу назвать общие методы подстановочных типов и не понимаю почему?Как вызвать общий метод подстановочного типа в Java?

public class GenericsTry2 { 

    public static class Element { 

     private Container<? extends Element> container; 

     public Container<? extends Element> getContainer() { 
      return container; 
     } 

     public void setContainer(Container<? extends Element> container) { 
      this.container = container; 
     } 

     public void doStuff() { 
      getContainer().doStuff(this); // how to call this? 
     } 
    } 

    public static class SomeSubElement extends Element { 
    } 

    public static class SomeSubElement2 extends Element { 
    } 

    public static class Container<E extends Element> { 

     public void doStuff(E element) { 
     } 

    } 

    public static void main(String[] args) { 

     Container<SomeSubElement2> c = new Container<SomeSubElement2>(); 

     Element e = new SomeSubElement(); 

     c.doStuff((SomeSubElement2) e); // still can do this at compile time this way 

    } 


} 

ответ

1

Container<E extends Element> означает, что он содержит что-то E, который простирается от Element. Не обязательно сам Element.

Представьте, что произойдет, если вы будете иметь:

Container<RedElement> redElements = new Container<RedElement>(); 
Container<E extends Element> container = redElements; 

// According to your logic it will work 
container.add(new Element()); 

// Problem here. You just put Element there which is not RedElement. 
RedElement element = container.getFirst(); 
9

Container<? extends Element> Имея означает, что the Container can only produceElement(s), but cannot consumeElement(s).

Причина этого заключается в том, что ? extends Element обозначает целое семейство неизвестных подтипов Element. Предположим, вы установили свой контейнер в Container<SomeSubElement>. Затем, минуя в контейнер (даже вы знаете, что это Element, или подтип Element) будет неправильным, потому что this может быть или не быть SomeSubElement (в зависимости от типа времени выполнения).

В мире Generics это называется со-дисперсией.

Для того, чтобы компиляция кода (я не гарантирую, что вам нужно именно это), вы можете сделать (обратите внимание, что я изменил контейнер, чтобы быть потребителем Element (s) вместо производителя):

public class Element { 

    private Container<? super Element> container; 

    public Container<? super Element> getContainer() { 
     return container; 
    } 

    public void setContainer(Container<? super Element> container) { 
     this.container = container; 
    } 

    public void doStuff() { 
     getContainer().doStuff(this); 
    } 
} 

Однако, если вам нужен ваш Container быть производителем и потребителем в то же время, просто избавиться от шаблона и параметризация его только <Element>.

+0

Я согласен с вами в словах 'SubSubElement'. Пожалуйста, посмотрите мой обновленный код. Я создал «SubSubElement», и это был родной брат, и я смог ошибиться в методе 'main'. Это вызывает исключение во время выполнения. Поэтому я не понимаю, почему это должно вызвать ошибку времени компиляции в первом случае? –

+0

Ну, это естественно. 'SomeSubElement' не может быть' SomeSubElement2', и вы получаете исключение Runtime Exception, потому что приведение происходит в Runtime. С явным приложением вы указываете компилятор * «Я знаю, что тип' e' в Runtime будет совместим с 'SomeSubtype2', поэтому, пожалуйста, доверяйте мне и скомпилируйте мой код" *, однако 'SomeSubElement' несовместим с' SomeSubElement2 'и, следовательно, вы получаете исключение .. –

0

Метод getContainer() Метод гарантирует возврат некоторого объекта класса, который расширяет элемент. Поэтому компилятор в этот момент не знает, что возвращаемое значение действительно имеет тип Container. Если вы хотите позвонить в doStuff() -Мотод класса Container, вы должны указать результат getContainer() на тип Container явно.

((Container) getContainer()).doStuff(this); 
Смежные вопросы