2010-10-16 5 views
6

В качестве практического примера общего вопроса в теме, я хотел бы реализовать метод containsAll в интерфейсе Set среализации интерфейса с помощью метода аргумента Суперклассов

public boolean containsAll(Iterable<?> c) { /* ... */ } 

Я полагаю, это должно быть разрешено, так как Collection - Iterable, что означает, что containsAll будет охватывать требования к интерфейсу. Точно так же, в более общем плане возможность реализации интерфейсов с суперклассами аргументов кажется, что она должна работать.

Однако Eclipse не говорит (не пробовал просто джавак прямо) - может кто-нибудь объяснить причину этого? Я уверен, что есть что-то в спецификации, которая делает это так, как есть, но я хотел бы также понять мотивацию требования. Или я пропустил что-то вроде Iterable<?>, не являясь суперклассом Collection<?>?

В качестве побочного вопроса - учитывая, что я объявляю два метода, метод с сигнатурой Iterable всегда будет предпочтительнее при вызовах с аргументом Collection?

Затмение Ошибка:

Если удалить метод с Collection подписи, просто оставить Iterable один (см после ошибки), я получаю следующее:

The type BitPowerSet must implement the inherited abstract method Set<Long>.containsAll(Collection<?>)

Конкретная реализация бытия :

@Override public boolean containsAll(Collection<?> c) { 
    for (Object o : c) if (!contains(o)) return false; 
    return true; 
} 
public boolean containsAll(Iterable<?> c) { 
    for (Object o : c) if (!contains(o)) return false; 
    return true; 
} 
+0

Не могли бы вы предоставить сообщение об ошибке Eclipse дает вам? Работает для меня в IDEA. –

+0

@ Никита: отредактирован. Soooo ... это может быть просто вещь Eclipse? – Carl

+0

Этот кошмар терминологии. Я убегаю от таких проблем. – skaffman

ответ

2

Моя догадка, почему Java имеет такое ограничение есть, скажем, у вас есть:

class A { 
    void foo(String s) { ... } 
} 

class B extends A { 
    // Note generalized type 
    @Override void foo(Object s) { ... } 
} 

Теперь, если у вас есть class C extends B и он хочет, чтобы переопределить foo, не ясно, какой аргумент он должен принять.

Скажет, например, C продлил непосредственно на первом, перекрывая void foo(String s), а затем он был изменен, чтобы расширить B. В этом случае C Существующего переопределение foo станет недействительным, поскольку Б foo должен иметь возможность обрабатывать все Object с, не только String s.

+0

ах, это кажется разумным объяснением - расширение интерфейса потребует, чтобы под-классы поддерживали расширенный интерфейс. Тем не менее, похоже, что это должно быть разрешено - обычно подклассам не разрешается сужать интерфейсы. – Carl

+0

Это также, вероятно, делает конструкцию виртуальной таблицы более ясной, чтобы утверждать, что все методы в ней имеют точную подпись. Или, может быть, они просто подумали, что они могут заставить его работать, если они хватают его достаточно, но не видели убедительной необходимости и оставили его. – oksayt

+0

Мне что-то не хватает - этот пример должен работать? В аннотации @Override я получаю «Метод не отменяет метод из своего суперкласса». – Amalgovinus

5

Поскольку интерфейс, который вы реализуете, объявляет (аннотация) метод containsAll(Collection<?>), вы должны реализовать его с помощью этой точной подписи. Java не позволяет вам реализовать/переопределить метод с более широким типом параметра, чем оригинал. Вот почему вы получаете сообщение об ошибке, когда вы закомментируете свой метод с помощью подписи Collection.

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

+0

Ошибка, когда метод не закомментирован. Только тогда, когда подпись с подписью «Collection». – Carl

+0

Также можно понять, почему это так? Это в соответствии с ответом @ oksayt? – Carl

+0

@ Карл, вы имеете в виду, почему Java спроектирован так? Может быть. –

0

Типы аргументов являются частью сигнатуры метода, поэтому jvm нуждается в методе с точной одной и той же сигнатурой для поиска переопределений. A containsAll (Iterable) будет иметь другую подпись, чем containsAll (Collection).

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

К вашему второму вопросу компилятор предпочтет аргумент Collection, поскольку он является подтипом Iterable, что делает метод Collection более конкретным, чем Iterable.