List и ArrayList, характерное поведение в generics [duplicate]

В RegEx {i,f} означает, что «между i и f соответствует». Давайте посмотрим на следующие примеры:

  • {3,7} означает от 3 до 7 соответствий
  • {,10} означает до 10 совпадений без нижнего предела (т.е. нижний предел равен 0)
  • {3,} означает не менее 3 совпадений без верхнего предела (т.е. верхний предел бесконечен)
  • {,} означает, что верхний предел или нижний предел для количество совпадений (т. е. нижний предел равен 0, а верхний предел бесконечен)
  • {5} означает ровно 4

Большинство хороших языков содержат сокращения, RegEx:

  • + является сокращением для {1,}
  • * является сокращением для {,}
  • ? является сокращенное значение для {,1}

Это означает, что + требует не менее 1 совпадения, в то время как * принимает любое количество совпадений или не имеет совпадений вообще, а ? принимает не более 1 совпадающие или нулевые.

Кредит: Codecademy.com

168
задан Adam Michalik 5 April 2016 в 09:57
поделиться

2 ответа

CharSequence - interface. Поэтому, даже если SomeClass не реализует CharSequence, было бы вполне возможно создать класс

class SubClass extends SomeClass implements CharSequence

. Поэтому вы можете написать

SomeClass c = getCharSequence();

, потому что предполагаемый тип X является типом пересечения SomeClass & CharSequence.

Это немного странно в случае Integer, потому что Integer является окончательным, но final не играет никакой роли в этих правилах. Например, вы можете написать

<T extends Integer & CharSequence>

. С другой стороны, String не является interface, поэтому было бы невозможно расширить SomeClass, чтобы получить подтип String, потому что java не поддерживает множественное наследование для классов.

С примером List вам нужно помнить, что дженерики не являются ни ковариантными, ни контравариантными. Это означает, что если X является подтипом Y, List<X> не является ни подтипом, ни супертипом List<Y>. Поскольку Integer не реализует CharSequence, вы не можете использовать List<Integer> в своем методе doCharSequence.

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

<T extends Integer & CharSequence> void foo(List<T> list) {
    doCharSequence(list);
}  

Если у вас есть метод, который возвращает a List<T> следующим образом:

static <T extends CharSequence> List<T> foo() 

вы можете сделать

List<? extends Integer> list = foo();

Опять же, это потому, что выведенные type Integer & CharSequence, и это подтип Integer.

Типы пересечений возникают неявно, когда вы указываете несколько границ (например, <T extends SomeClass & CharSequence>).

Для получения дополнительной информации здесь является частью JLS, где объясняется, как работают ограничения типов. Вы можете включить несколько интерфейсов, например

<T extends String & CharSequence & List & Comparator>

, но только первая оценка может быть неинтерфейсом.

180
ответ дан Paul Boddington 15 August 2018 в 18:40
поделиться
  • 1
    Я понятия не имел, что вы можете поместить & в общее определение. +1 – flakes 4 April 2016 в 12:52
  • 2
    @flkes Вы можете поместить более одного, но только первый аргумент может быть неинтерфейсом. <T extends String & List & Comparator> в порядке, но <T extends String & Integer> нет, потому что Integer не является интерфейсом. – Paul Boddington 4 April 2016 в 12:53
  • 3
    @PaulBoddington Для этих методов существует практическое применение. Например, если тип фактически не используется для сохраненных данных. Примерами этого являются Collections.emptyList(), а также Optional.empty(). Эти возвратные реализации общего интерфейса, но ничего не хранить. – Stefan Dollase 4 April 2016 в 13:36
  • 4
    И никто не говорит, что во время компиляции класс final будет final во время выполнения. – Holger 4 April 2016 в 15:25
  • 5
    @Federico Peralta Schaffner: точка здесь, метод getCharSequence() обещает вернуть все, что X требует вызывающего, включая возврат типа расширения Integer и реализации CharSequence, если вызывающий абонент нуждается в этом и в соответствии с этим обещанием, правильно разрешить присвоение результата Integer. Это метод getCharSequence(), который сломан, поскольку он не выполняет свое обещание, но это не ошибка компилятора. – Holger 4 April 2016 в 15:45

Тип, который выводится вашим компилятором до назначения для X, - Integer & CharSequence. Этот тип чувствует странным, потому что Integer является окончательным, но он является вполне допустимым типом в Java. Затем он переключается на Integer, что совершенно нормально.

Существует только одно возможное значение для типа Integer & CharSequence: null. С помощью следующей реализации:

<X extends CharSequence> X getCharSequence() {
    return null;
}

Следующее назначение будет работать:

Integer x = getCharSequence();

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

Реальная проблема - это API, а не сайт вызова

. На самом деле, я недавно написал блог об этом API-шаблоне API . Вы должны (почти) никогда не разрабатывать общий метод для возврата произвольных типов, потому что вы можете (почти) никогда не гарантировать, что выводимый тип будет доставлен. Исключением являются такие методы, как Collections.emptyList(), в случае которых пустота списка (и стирание родового типа) является причиной того, что любой вывод для <T> будет работать:

public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}
57
ответ дан Lukas Eder 15 August 2018 в 18:40
поделиться
  • 1
    Короткий и сладкий ответ. Отлично сработано – Jochen Bedersdorfer 5 April 2016 в 17:13
Другие вопросы по тегам:

Похожие вопросы: