Наследование и приведение типов объектов List - PullRequest
5 голосов
/ 15 июня 2011

У меня проблемы с приведением Списка фруктов к подклассу фруктов, содержащемуся в Списке.

public class Response {
    private List<Fruit> mFruitList;
    public List<Fruit> getFruitList() {
        return mFruitList;
    }
} 

public class Fruit {
}

public class Orange extends Fruit {
}

List<Fruit> oranges = response.getFruitList();

Как я разливаю апельсины, чтобы это был Список класса Апельсин?Это плохой шаблон дизайна?В основном я получаю ответ JSON от сервера, который является списком фруктов.Для каждого конкретного вызова веб-службы я знаю, какой подкласс Fruit я получу, и поэтому мне нужно правильно составить этот список.

Ответы [ 5 ]

7 голосов
/ 15 июня 2011

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

public class Response<T extends Fruit> {
    private List<T> mFruitList;
    public List<T> getFruitList() {
        return mFruitList;
    }
} 

Response<Orange> response = // create
List<Orange> oranges = response.getFruitList();

РЕДАКТИРОВАТЬ: Под шаблонами я подразумевал дженерики.Извините, у меня сейчас слишком много C ++

3 голосов
/ 15 июня 2011

Вся идея Typecasts заключается в том, чтобы иметь возможность сказать компилятору: «Эй, я знаю об этом больше, чем вы».В вашем коде компилятор не может безопасно уменьшить List<Fruit> до List<Orange>, потому что он не может знать, что список будет содержать во время выполнения.

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

List<Orange> oranges = (List<Orange>) response.getFruitList();

Компилятор, конечно, предупредит вас, поскольку вы что-то делаетеон думает, что ты не должен делать.И просто знайте, что JVM может посмеяться последним, бросив CastClassException, если вы ошиблись!

1 голос
/ 15 июня 2011

Думайте о дженериках как о воротах для того, какие типы объектов может содержать список.Из-за этого наследования и приведения не будет работать так, как вы ожидаете.В приведенном вами примере вы можете поместить как апельсины, так и яблоки в свой <code>List<Fruit>.Если в списке есть как яблоки, так и апельсины, как вы можете разыграть его до <code>List<Orange>.

Если вам нужен <code>List<Orange>, зачем вообще беспокоиться о <code>List<Fruit>.Если вы все равно явно используете его, и вы точно знаете, что он содержит, это, вероятно, ненужная абстракция.

Если вы работаете с API, который вы не можете изменить, но вы точно знаете, что он содержит, вам следует выполнить цикл с проверкой instanceof только для того, чтобы убедиться, что каждый экземпляр Fruit и Explicity приведен к Orangeкогда вам нужно Orange API.

0 голосов
/ 15 июня 2011

Дано

List<Fruit> getFruits() {...}

Вы не можете выполнить тип

List<Orange> oranges = (List<Orange>) getFruits();

Из-за стирания типа во время выполнения тип getFruits просто List.Компилятор даже не позволит вам сделать уныние (у меня были сомнения, поэтому я попробовал в Eclipse, прежде чем ответить).

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

List<? extends Fruit> getFruits() {...}

Тогда приведение становится возможным,но с предупреждением о безопасности типа:

@SuppressWarnings("unchecked")
List<Orange> oranges = (List<Orange>) getFruits();

Учитывая, что тип времени выполнения getFruits равен List, вы можете просто отбросить информацию о типовых типах и использовать небезопасное назначение:

@SuppressWarnings("unchecked")    
List<Orange> oranges = (List) getFruits();

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

List<Orange> oranges = Arrays.asList((Orange[])getFruits().toArray())

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

0 голосов
/ 15 июня 2011

Вы должны разыграть List и проверить, являются ли каждый элементы instanceof Orange, и после проверки разыгрыша в Апельсинах. Это «лучшая практика».

...