Что я делаю не так с дженериками Java в этом интерфейсе и методе? - PullRequest
1 голос
/ 05 января 2011

РЕДАКТИРОВАТЬ: Здесь есть еще одна складка, которую я пропустил, которая, как оказалось, имеет большое значение. Сигнатура метода doAnotherThing выглядит следующим образом:

<T extends Bar> T doAnotherThing(List<Foo<T>> foo) {
    return foo.get(0).doSomething();
}

Не обращайте внимания на тот факт, что это List, просто обратите внимание на тот факт, что List - это универсальный класс / интерфейс. Я вызываю метод так:

doAnotherThing(new ArrayList<FooImpl>);

Итак, у меня есть класс и интерфейс, определенные так:

abstract class Bar {
    // some neat stuff
}

class BarImpl extends Bar {
    // some cool stuff
}

interface Foo<T extends Bar> {
    T doSomething();
}

class FooImpl implements Foo<BarImpl> {
    BarImpl doSomething() {
        // Does something awesome
    }
}

Это все прекрасно, денди и прекрасно работает.

Теперь у меня есть такой метод:

<T extends Bar> T doAnotherThing(List<Foo<T>> foo) {
    return foo.get(0).doSomething();
}

Этот метод является универсальным методом в совершенно другом классе и не входит в приведенную выше цепочку.

Однако, когда я пытаюсь использовать этот метод следующим образом, я получаю сообщение об ошибке, говорящее, что типы не совпадают:

doAnotherThing(new FooImpl());

FooImpl реализует Foo<T>, так что я не вижу, как это ошибка? Может я что то недопонимаю? Спасибо

Ответы [ 4 ]

3 голосов
/ 05 января 2011

Ответ изменен, чтобы отразить разъяснение в вопросе

Ошибка компиляции (несоответствие типов) на самом деле совершенно корректна. Обратите внимание, что определение метода говорит, что параметр является List<Foo<T>>. Это означает, что список может содержать any Foo<T>, и метод должен даже иметь возможность добавить любой объект, который реализует Foo<T>. И это не тот случай, когда вы даете ему List<FooImpl>, потому что это может содержать только экземпляры FooImpl. Это работает:

doAnotherThing(new ArrayList<Foo<BarImpl>>());

Смешивание дженериков и полиморфизма быстро приводит к очень сложным сценариям, поэтому это следует делать экономно.

2 голосов
/ 05 января 2011

У меня работает. Я подозреваю, что причина ошибки - это не та версия, которую вы указали выше. Компилируется ли приведенный ниже код (все в одном файле FooBarBaz.java)? Единственное изменение, которое я должен был сделать в вашем коде, было сделать FooImpl.doSomething() публичным. (О, и заставь это вернуть что-то. :))

public class FooBarBaz {

    <T extends Bar> T doAnotherThing(Foo<T> foo) {
        return foo.doSomething();
    }

    public static void main(String[] args) {
        new FooBarBaz().doAnotherThing(new FooImpl());
    }
}

abstract class Bar {
    // some neat stuff
}

class BarImpl extends Bar {
    // some cool stuff
}

interface Foo<T extends Bar> {
    T doSomething();
}

class FooImpl implements Foo<BarImpl> {
    public BarImpl doSomething() {
        return null;
    }
}

У меня отлично работает с IDEA 9, JDK 1.6 на Mac OS 10.6.5.

1 голос
/ 05 января 2011

Я считаю, что мой первоначальный ответ неверен.Кажется, что следующее работает нормально.Просто сделайте метод doSomething общедоступным, и он прекрасно компилируется под Java6.

abstract class Bar {
    // some neat stuff
}

class BarImpl extends Bar {
    // some cool stuff
}

interface Foo<T extends Bar> {
    public T doSomething();
}

class FooImpl implements Foo<BarImpl> {
    public BarImpl doSomething() {
        return null;
    }
}


public class Test {
<T extends Bar> T doAnotherThing(Foo<T> foo) {
    return foo.doSomething();
}

}
1 голос
/ 05 января 2011

Давайте предположим, BarImpl extends Bar, что, я полагаю, вы имели в виду с самого начала.

Как выглядит расширенный интерфейс Foo?

Это:

interface Foo<T extends Bar> {
    T doSomething();
    T doAnotherThing(Foo<T> foo);
}

?

В таком случае все работает со следующим импл:

 class FooImpl implements Foo<BarImpl> {

 public BarImpl doSomething() {
  return null;
 }

 public BarImpl doAnotherThing(Foo<BarImpl> foo) {
  return null;
 }

}

Теперь может возникнуть проблема, если вы определили это иначе:

interface Foo<T extends Bar> {
    T doSomething();
    <T extends Bar> T doAnotherThing(Foo<T> foo);
}

Поскольку такой способ определения doAnotherThing вводит еще один универсальный параметр.Что сбивает с толку параметр интерфейса и один из метода с общим именем, то есть T.(Кстати, я был удивлен, узнав, что Java допускает такое запутанное столкновение имен)

Последнее определение можно заменить на:

interface Foo<T extends Bar> {
    T doSomething();
    <Y extends Bar> Y doAnotherThing(Foo<Y> foo);
}

, что делает более понятным, почему public BarImpl doAnotherThing(Foo<BarImpl> foo) не являетсяправильный способ переопределения этого метода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...