2013-11-23 5 views
4

Java. Подстановочные знаки в коллекцияхJava. Подстановочные знаки в коллекциях

У меня возникли проблемы с пониманием подстановочных знаков в коллекциях, даже после прочтения подобных сообщений в Stack Overflow и различных сайтах учебников. Ниже я привел очень простой пример. Можете ли вы объяснить, как я выбрал бы между Collection<myClass>, Collection< ? Extends MyClass > и Collection< ? Super MyClass >?

package z5; 
import java.util.ArrayList; 
public class Z5 { 
public static class Animal{ 
} 
public static class Mammal extends Animal{ 
} 
public static class Reptile extends Animal{ 
} 
public static class Lion extends Mammal{ 
} 
public static class Tiger extends Mammal{ 
} 
public static class Snake extends Reptile{ 
}   
    public static void main(String[] args) { 
     ArrayList<Mammal> catHouse1 = new ArrayList<Mammal>(); 
      catHouse1.add(new Lion()); 
      catHouse1.add(new Tiger()); 
      catHouse1.add(new Mammal()); 
      catHouse1.add(new Animal()); //ERROR 
     ArrayList<? super Mammal> catHouse2 = new ArrayList<Mammal>(); 
      catHouse2.add(new Lion()); 
      catHouse2.add(new Tiger()); 
      catHouse2.add(new Mammal()); 
      catHouse2.add(new Animal()); //ERROR 
     ArrayList<? extends Mammal> catHouse3 = new ArrayList<Mammal>(); 
      catHouse3.add(new Lion()); //ERROR 
      catHouse3.add(new Tiger()); //ERROR 
      catHouse3.add(new Mammal()); //ERROR 
      catHouse3.add(new Animal()); //ERROR 
     ArrayList<Mammal> zooMammals = new ArrayList<Mammal>(); 
      zooMammals.addAll(catHouse1); 
      zooMammals.addAll(catHouse2); //ERROR 
      zooMammals.addAll(catHouse3); 
     ArrayList<Animal> zooAnimals = new ArrayList<Animal>(); 
      zooAnimals.addAll(catHouse1); 
      zooAnimals.addAll(catHouse2); //ERROR 
      zooAnimals.addAll(catHouse3);   
    } 
} 

В приведенном выше примере я делаю иерархию классов. Животное - это суперкласс Mammal and Reptile, а Mammal - суперкласс Lion и Tiger.

Я делаю ArrayLists для трех домов кошки в зоопарке. Первый - это просто ArrayList < Млекопитающее>. Я могу добавить любой объект типа Mammal или его подклассы.

Во-вторых, ArrayList <? супер млекопитающее>. Я также могу добавить любой объект типа Mammal или его подклассы.

Третий ArrayList <? распространяет млекопитающее>. Я ничего не могу добавить к этому.

Наконец, я добавляю свои три дома для кошек в коллекции зоопарков. Животные и млекопитающие являются главными суперклассами здесь, и поведение одинаково независимо от того, какой тип хранит ArrayList получателя. ArrayList < Mammal> и ArrayList могут быть добавлены в зоопарки. ArrayList не может.

Вот мои вопросы:


1) Если я хочу, чтобы создать массив, который содержит все подклассы определенного суперкласса, зачем мне нужен подстановочные? Не могу я просто объявить все ArrayList < Суперкласс> и получить необходимую мне функциональность?

2) Я понимаю, что «<? Extends superclass>» принимает суперкласс и все его подклассы. Ergo, <? простирается Млекопитающее> «принимает млекопитающих, львов и тигров. Это звучит точно так же, как« < Млекопитающее ». В чем разница?

3) Я прочитал, что« <? super className> "принимает любой класс, который является суперклассом классаName. Это звучит не так. В приведенном выше примере Lions не являются суперклассами млекопитающих, но« <? супер млекопитающее> "принимает Львы. Животные - суперклассы Млекопитающих, но" <? супер млекопитающее> «не принимает его. Я думаю, что у меня есть неправильная информация.

4) Если« <? extends superclass> "доступен только для чтения, как я могу его заполнить? И в чем смысл иметь пустой список, из которого вы можете только читать?

5) Почему метод addAll не работает на« < ? super className> "?

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

+1

http://docs.oracle.com/javase/tutorial/java/generics/ – Basilevs

ответ

3

Я не буду вдаваться в подробности.

List<Animal> означает: этот список содержит экземпляры Animal. Таким образом, вы можете добавить любое Животное, которое хотите, и вы обязательно получите Animal при получении элементов из этого списка.

List<? extends Animal> означает: этот список является общим списком, но мы не знаем тип родового типа. Все, что мы знаем об этом, это то, что общий тип - Animal или любой подкласс Animal. Таким образом, это может быть List<Animal>, например, List<Reptile> или List<Lion>. Но мы не знаем. Таким образом, при получении элемента из этого списка вы гарантированно получаете Animal. Но вам не разрешается добавлять что-либо в список, потому что вы не знаете его типа. Если вам было разрешено что-то добавить, вы можете сохранить Lion в List<Reptile>, и этот список больше не будет безопасным для типов.

List<? super Animal> означает: этот список является общим списком, но мы не знаем тип родового типа. Все, что мы знаем об этом, это то, что общий тип - Animal или любой суперкласс или супер интерфейс Animal. Например, это может быть List<Animal> или List<Object>. Таким образом, при получении элемента из этого списка все, что вы можете сказать наверняка, это то, что это объект. То, что вы можете сделать наверняка, состоит в том, чтобы добавить любое Животное, которое вы хотите в этом списке, хотя, поскольку оба List<Animal> и List<Object> принимают животных, лев, рептилий или любой другой подкласс Animal.

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

Когда метод принимает список как аргумент и его интересует только чтение из списка (т. Е. Список является производителем), вы будете использовать List<? extends Animal>. Это делает ваш метод более многократного использования, так как он может быть использован с List<Animal>, но и с List<Reptile>, в List<Lion> и т.д.

Когда метод принимает список в качестве аргумента и заинтересован только в добавлении элементов в список (т.е. список является потребителем), вы будете использовать List<? super Animal>. Это делает ваш метод более многоразовым, потому что его можно использовать с List<Animal>, но также с List<Object>.

Это правило известно как PECS: Procucer: Extends; Потребитель: Супер.

Если ваш метод принимает список в качестве аргумента и должен как получать, так и добавлять элементы в/из списка, вы будете использовать List<Animal>.

+0

«В большинстве случаев вы используете эти подстановочные знаки для аргументов своих методов». Спасибо Спасибо спасибо. Я пытался понять это из создания коллекции конкретных типов, и я не мог найти повод для этого (все еще не могу). Но я не думал об этом как о типе аргумента для метода. Подстановочные знаки делают методы более полезными, поскольку они позволяют использовать их для разных типов коллекций. – Bagheera

+0

Итак, в моем основном методе хранятся списки рептилий и млекопитающих и земноводных, чтобы отслеживать все в зоопарке, я должен объявить эти типы списков как их особые суперклассы: Список < Mammal >, Список < Reptile >, Список < Amphibian > и т. Д. – Bagheera

+0

Если у меня есть методы, которые я хочу использовать для всех животных, я должен объявить их аргументы с помощью подстановочных знаков: public double totalWeight (List l) {// цикл foreach, чтобы добавить вес каждого животного объекта и вернуть средний} – Bagheera

1

Если вы просто хотите список, который может содержать Animal или подклассы Animal вы должны использовать List<Animal>. Я думаю, что это то, что вам нужно больше всего времени.

продолжается:

ArrayList<? extends Mammal> list = .. 

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

ArrayList<? extends Mammal> list = new ArrayList<Tiger>(); // List of type Tiger 

При использовании <? extends Mammal> вы не можете вставить что-либо в list (кроме null). Причина в том, что нет никакого типичного способа вставить что-либо. Вы не можете добавить Mammal в list, потому что list может быть типа Tiger (как в примере выше). A Mammal нет Tiger поэтому это не может работать.

Вы не можете добавить Tiger в list. Это потому, что <? extends Mammal> ничего не говорит о Tiger. Также возможно, что list имеет тип Lion.

супер:

С <? super Mammal> это противоположная ситуация. <? super Mammal> означает, что список имеет тип Mammalили один из его суперклассов. Вы можете сделать это:

ArrayList<? super Mammal> list = new ArrayList<Animal>(); // List of type Animal 

Вы можете добавить любой подкласс Mammal к этому списку. Это безопасно, поскольку подклассы Mammal всегда являются подклассами Animal. Однако вы не можете получить что-либо из списка с другим типом, кроме Object. Это потому, что вы не знаете точный тип списка.

Mammal m = list.get(0); // can't work because list can be a List<Animal> 
Смежные вопросы