Java Универсальный метод / типы параметров - PullRequest
3 голосов
/ 03 декабря 2011

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

interface Eatable{ public void printMe();}
class Animal { public void printMe(){System.out.println("Animal object");}}
class Dog extends Animal implements Eatable{ public void printMe(){System.out.println("Dog object");}}
class BullTerrier extends Dog{ public void printMe(){System.out.println("BullTerrier object");}}

public class ZiggyTest{

    public static void main(String[] args) throws Exception{

        Object[] objArray = new Object[]{new Object(), new Object()};
        Collection<Object> objCollection = new ArrayList<Object>();

        Animal[] animalArray = new Animal[]{new Animal(),new Animal(),new Animal()};
        Collection<Animal> animalCollection = new ArrayList<Animal>();      

        Dog[] dogArray = new Dog[]{new Dog(),new Dog(),new Dog()};
        Collection<Dog> dogCollection = new ArrayList<Dog>();

        System.out.println(forArrayToCollection(animalArray,animalCollection).size());
        // System.out.println(forArrayToCollection(dogArray,dogCollection).size());  #1 Not valid

        System.out.println(genericFromArrayToCollection(animalArray,animalCollection).size());
        System.out.println(genericFromArrayToCollection(dogArray,dogCollection).size());  


        System.out.println(genericFromArrayToCollection(animalArray,objCollection).size()); //#2 
        System.out.println(genericFromArrayToCollection(dogArray,animalCollection).size()); //#3 
        // System.out.println(genericFromArrayToCollection(objArray,animalCollection).size()); //#4

    }

    public static Collection<Animal> forArrayToCollection(Animal[] a, Collection<Animal> c){
        for (Animal o : a){
            c.add(o);
        }

        return c;
    }

    static <T> Collection<T> genericFromArrayToCollection(T[] a, Collection<T> c) {
        for (T o : a) {
            c.add(o); 
        }

        return c;
    }

}

Почему компилятор допускает вызов метода genericFromArrayToCollection(), только если объявленный тип коллекции является родителем объявленного типа массива (Смотрите строки, отмеченные # 2, # 3 и # 4).Почему это так?

Спасибо

Редактировать

Когда я раскомментирую строку, помеченную # 4, я получаю следующую ошибку

ZiggyTest.java:34: <T>genericFromArrayToCollection(T[],java.util.Collection<T>) in ZiggyTest cannot be applied to (java.lang.Object[],java.util.Collection<Animal>)
                System.out.println(genericFromArrayToCollection(objArray,animalCollection).size()); //#4
                                   ^
1 error

Редактировать 2

@ Tudor Я попробовал следующий метод, используя это утверждение

System.out.println(method1(new ArrayList<String>()).size());

Компилятор пожаловался на ошибку, сказав, что не может быть применен к java.util.ArrayList

public static Collection<Object> method1(ArrayList<Object> c){
        c.add(new Object());
        c.add(new Object());        
        return c;
}

Ответы [ 3 ]

1 голос
/ 03 декабря 2011

Чтобы ответить на ваш вопрос, давайте сначала установим предпосылку: мы знаем, что если у вас есть метод, принимающий массив в качестве параметра, вы можете передать массив подтипа:

public void method(Object[] list) // can be called with String[]

, но наоборотне соответствует действительности:

public void method(String[] list) // cannot be called with Object[]

Затем все сводится к тому, как фактически создаются параметры универсального метода, то есть как работает вывод типа параметра.В вашем случае # 3 это выводится для Animal, поэтому объявление метода действительно выглядит следующим образом:

static Collection<Animal> genericFromArrayToCollection(Animal[] a, 
                                                       Collection<Animal> c) {

, и, поскольку Dog является подтипом Animal, он может хорошо соответствовать вместо Animal[] массив.То же самое для случая № 2.

Однако в случае № 4 тип снова выводится как Animal, поэтому метод выглядит как выше, но вы не можете поместить массив Object[] вместо Animal[] массив, потому что Object не является подтипом Animal.

1 голос
/ 03 декабря 2011

в основном, genericFromArrayToCollection () определяет T как параметр типа, который используется для определения 2 параметров метода (т. Е. T[] a и Collection<T> c).И a, и c должны быть основаны на одном и том же типе, поэтому Dog[] и Collection<Dog> будут работать, Animal[] и Collection<Animal> будут работать, но Object[] и Collection<Animal> не будут работать, потому что теперь T является Object, тогда какКоллекция основана на животных.Если бы у подписи вашего метода было Collection<? extends T>, это, возможно, сработало бы, я думаю.

1 голос
/ 03 декабря 2011

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

Для forArrayToCollection.

Animal[], Collection<Animal>
Dog[], Collection<Dog>

Этот метод имеет типы параметров Animal[], Collection<Animal>.Первый звонок точно соответствует.Второй вызов пытается присвоить Collection<Animal> Collection<Dog>, что неправильно (я могу добавить Cat к Collection<Animal>, что я не должен делать с Collection<Dog>).

Для genericFromArrayToCollection.

Animal[], Collection<Animal>
Dog[], Collection<Dog>

Animal[], Collection<Object>
Dog[], Collection<Animal>
Object[], Collection<Animal>

Во всех случаях T должен заменять универсальный аргумент коллекции.Первые два вызова совпадают точно.Для третьего вызова T - это Object, и из-за странного поведения массивов в Java Animal[] можно назначить для Object[] (но вы получите непроверенный ArrayStoreException, если попытаетесьхранить NumberFormat в нем).Аналогично, для четвертого Dog[] может быть назначено Animal[].Для последнего Object[] нельзя присвоить Animal[] (при чтении из массива никогда не должно получаться ClassCastException, если вы не исправили недосказанность с помощью общего чудовища приведения к массиву (вы получите предупреждение javac - обратите внимание наон)).

...