Дженерики Java, похоже, работают по-разному для классов, чем для методов

Я после этого:

http://rickyclarkson.blogspot.com/2006/07/duck-typing-in-java-and-no-reflection.html

и я пытаюсь адаптировать этот:

<T extends CanQuack & CanWalk> void doDucklikeThings(T t)
{
    t.quack();
    t.walk();
}

для этого:

public class Activate<D extends CanQuack & CanWalk> {

    D d = new MyWaterFowl(); //Type mismatch
}

хотя MyWaterFowl реализует эти интерфейсы.

Я хотел бы решение, которое никогда не упоминает MyWaterFowl в, так как я собираюсь в конечном итоге просто впрыснуть его (или что-нибудь еще, что реализует эти межфазные границы.)


Если ваш ответ в основном "вы не можете этого сделать,какой тип это будет?". Пожалуйста, объясните, почему он работает для метода doDucklikeThings и окончательно заявить, если это невозможно сделать то же самое с классом или, если это возможно, как это сделать.

T в doDucklikeThings должно быть что-то действительное, так как оно работает. Если бы я сдал это в класс, что бы я сдал?


как спрошено здесь Код MyWaterFowl:

package singleResponsibilityPrinciple;

interface CanWalk { public void walk(); }
interface CanQuack { public void quack(); }
interface CanSwim { public void swim(); }


public class MyWaterFowl implements CanWalk, CanQuack, CanSwim {

    public void walk() { System.out.println("I'm walkin` here!"); }
    public void quack() { System.out.println("Quack!"); }
    public void swim() { System.out.println("Stroke! Stroke! Stroke!"); }
}

помните, я подтвердил, что doDucklikeThings работает. Мне нужен синтаксис, который позволит мне вводить все, что реализует необходимые интерфейсы.

6 ответов


это не работает, потому что класс / метод является общим, и вызывающий ваш класс / метод может установить D to MyAmericanEagle.

 Activate<MyAmericanEagle> active = new Activate<>();

тогда ваш код приведет к

 MyAmericanEagle d = new MyWaterFowl(); 

поскольку это не имеет смысла (приведет к ClassCastException), компилятор отклоняет его.


// Type mismatch

хотя MyWaterFowl реализует эти интерфейсы.

дело не в типе D реализация этих интерфейсов (и/или расширение класса). Переменная универсального типа привязана к конкретному аргументу типа. Этот тип может полностью отличаться от MyWaterFowl поэтому вы не можете использовать их как взаимозаменяемые.


чтобы ответить на ваши правки, вы делаете две совершенно разные вещи в двух фрагменты. Переменная type объявляется с некоторыми ограничениями. Поэтому гарантируется, что это тип, который реализует некоторый интерфейс (или расширяет некоторый класс), но вы не знаете, какой тип это в любом случае.


я хочу прояснить две вещи, которые вы сделали, т. е.. что вы ожидаете в своем вопросе и решении, которое вы дали в своем ответе.

Дженерики-это функция времени компиляции, где сервер код, для пример

class Activate<D extends CanWalk & CanQuack> {
    D instance;
    public Activate(D d) {
        this.instance = d;
    }

    public D getInstance() {
        return instance ;
    }
}

объявляет типа переменная. Это переменная. В контексте объявления вы не знаете его точный тип во время компиляции.

на клиент код, например,

new Activate<>(new MyWaterFowl());

персонализация типа MyWaterFowl к переменной типа, объявленной в Activate. Так что клиент код знает, что D это во время компиляции.

если ниже

public D getInstance() {
    D someRef = new MyWaterFowl();
    return someRef;
}
в коде сервера это приведет к сбою
Activate<SomeOtherBird> activate = new Activate<>(new SomeOtherBird());
SomeOtherBird reference = activate.getInstance();

дженерики гарантируем, что getInstance() тип безопасен, потому что он объявлен как возвращающий любой тип, связанный с переменной типа D. В данном случае, это SomeOtherBird. Если getInstance() код выше был разрешен, тип безопасности будет нарушен как getInstance() вернет что-то другое, кроме того, что было связано с ним.

это не меняет того факта, что в вашем коде сервера (общий класс) вы знаете границы D, ie. это и CanQuack и CanWalk. Поэтому все, что может сделать объект этих типов, может сделать и объект, на который ссылается D переменная do.


это действительно можно сделать.

общий код между в порядке. Это не отличается для методов и классов. Просто нужно закончить инъекцию зависимостей:

public class Activate<D extends CanQuack & CanWalk> {
    private D d;

    Activate(D d) {
        this.d = d;
    }

    void doDuckLikeThings() {
        d.quack();
        d.walk();
        //d.swim(); //Doesn't work 
        //And shouldn't since that was the whole point of ISP.
    }
}

public class MainClass {
    public static void main(String[] args) {
       Activate<MyWaterFowl> a = new Activate<>(new MyWaterFowl());
       a.doDuckLikeThings();
    }
}

думал, что дам ответ, который говорит, что делать, чтобы исправить это.


тип класса, который расширяет объект, отличается от типа объекта. Это означает, что вы не можете создать экземпляр класса по его экстендеров.

когда вы компилируете это:

1    public class Sample<T extends Object> {
2      public Sample() {
3         T t = new Object();
4       }
5    }

у тебя есть incompatible types ошибка.

Sample.java:3: error: incompatible types
    T t = new Object();
          ^
  required: T
  found:    Object
  where T is a type-variable:
    T extends Object declared in class Sample
1 error

это то же самое, что и когда вы делаете это в непатентованной форме:

1 public class Sample{
2    public Sample() {
3    }
4 }
5
6 class SampleExtends extends Sample {
7   public SampleExtends() {
8
9    }
10 }
11
12 class User {
13    public User() {
14        SampleExtends se = new Sample();
15    }
16 }

вы получили эту ошибку от компилятора: несовместимые типы

Sample.java:14: error: incompatible types
    SampleExtends se = new Sample();
                       ^
  required: SampleExtends
  found:    Sample
1 error

что вы ожидаете от фактического типа D во втором фрагменте кода?

предположим, что-то делает это:

Activate<Daffy> myActivate = Activate<Daffy>();

что должно произойти потом? Это значит D должен иметь тип Daffy но вы пытаетесь установить d экземпляр MyWaterFowl.


Generics работает так же для классов, как и для методов.

Вы, кажется, немного неправильно понимаете дженерики. Параметр типа <T extends CanQuack & CanWalk> и не определение нового псевдоним типа действительно для области метода или класса, это заполнитель для некоторого типа, который позже заполняется кем-то другим.

для универсального метода, такого как ваш пример

<T extends CanQuack & CanWalk> void doDucklikeThings(T t)
{
    t.quack();
    t.walk();
}

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

doDuckLikeThings(myWaterfowl);

и компилятор угадывает, какой параметр типа вы хотите здесь (вероятно, тип myWaterfowl переменной).

для универсального класса, это код, который создает экземпляр класса, который определяет фактический тип параметра. Так что я могу сказать

Activate<Duck> duckActivator = new Activate<Duck>();

после этого duckActivator instance-экземпляр Activate<Duck>, и внутри это,D теперь обязательно Duck. Теперь, очевидно, ваше объявление переменной говорит

Duck d = new MyWaterfowl();

и это не сработает.

это не будет работать в общем методе, либо-это недопустимо, а также:

<T extends CanQuack & CanWalk> void doDucklikeThings()
{
    T t = new MyWaterfowl();
}

я хотел бы решение, которое никогда не упоминает MyWaterFowl на <>С тех пор, как я собираюсь в конечном итоге просто вводите его (или что-нибудь еще, что реализует эти интерфейсы).

тогда не упомяните его конструктор. Тот, кто вводит ваш объект типа утки, также должен решить фактический параметр типа-т. е. это должен быть тот, кто создает Activate экземпляра. И это не может измениться после строительства