2013-05-30 5 views
1

Это, вероятно, было задано раньше в той или иной форме, но я до сих пор не могу его решить, поэтому я подумал, что попрошу коллективную мудрость здесь. У меня такой интерфейс - я оставил некоторые комментарии, поскольку они могут помочь.Как избежать предупреждения об общем компиляторе для интерфейса

/** Technical note: this generic binding is known as F-bound where class 
* T is 'the worked-on class' by Splittable i.e. the implementing class. 
*/ 
interface Splittable <T extends Element & Splittable<T>> { 


/** Returns an object of the same class which has been split off at 
* the point given. 
*/ 
public T splitOff(double at); 


/** Appends the object provided, which must be of the same class, to 
* this object. 
*/ 
public void append(T el); 
} 

Итак, я работаю на элементе, который я не знаю, во время компиляции, таким образом:

if (el instanceof Splittable) { 
    if (nextEl.getClass == el.getClass) { 
     // same class so join them 
     ((Splittable)el).append(nextEl); 
    } 
} 

и вот где я получаю предупреждение компилятора - бесконтрольно вызов для добавления (T) как член сырого типа Splittable.

В подклассе Element я попытался как

SubClassEl extends Element implements Splittable 

и

SubClassEl extends Element implements Splittable<SubClassEl> 

без разницы в предупреждении.

Кроме того, в момент вызова добавления(), я попытался

((Splittable)el).splitOff(x).append(nextElement) 

но компилятор не распознает, что splitOff должен возвращать расщепляемое, только элемент.

Любые идеи?

+0

Можете ли вы показать нам, что «Элемент» расширяет и реализует? – vikingsteve

+0

Hi vikingsteve - не уверен, что это поможет: Элемент - это абстрактный класс, который я написал с помощью таких вещей, как getEditState, getHeight, которые, как я думаю, не имеют отношения к проблеме генериков. Спасибо за ваш интерес! –

ответ

4

Что здесь происходит, так это то, что вы используете необработанные типы, которые «отказываются» от проверки общего типа. Когда это происходит, T - erased - Element. От The Java Tutorials:

При использовании множественной привязки первый тип, упомянутый в ссылке, используется как стирание переменной типа.

Что касается того, как скомпилировать ваш код без предупреждений, это может быть невозможно с вашим текущим дизайном. Работа над простым Element s с instanceof и отливки не собираются хорошо смешиваться с параметром типа рекурсивно связанного типа, объявленным на Splittable.

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

static <T extends Element & Splittable<T>> void maybeAppend(T el1, Element el2) { 
    if (el1.getClass() == el2.getClass()) { 
     @SuppressWarnings("unchecked") // arguments' runtime types are equal 
     final T el2WithNarrowedType = (T)el2; 
     el1.append(el2WithNarrowedType); 
    } 
} 

... 

if (el instanceof Splittable<?>) { 
    maybeAppend(el, nextEl); //compiler error 
} 

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

Я ответил на аналогичную проблему: Passing a runtime resolved parameter to a method which has multiple bound type, compilation error. Ближайшее решение, не использующее необработанные типы, которые я мог придумать, было серьезным взломом и работало только над некоторыми компиляторами. ОП закончил использование необработанного типа.

Моя рекомендация к вам, чтобы избежать самостоятельного ввода и рассмотреть возможность реализации что-то вроде этого, вместо:

interface ElementSplitter<T extends Element> { 

    T splitOff(T element, double at); 

    void append(T element, T appendedElement); 
} 

Если реализация в splitOff и append функциональности требуется доступ к закрытым членам данного Element вывода, обходной путь должен был бы сделать это вложенный класс, например:

class SubClassEl extends Element { 

    ... 

    static class Splitter implements ElementSplitter<SubClassEl> { 
     ... 
    } 
} 

Я замечаю в Splittable Javadoc Вы писали:

Техническое примечание: это родовое связывание называется F переплете, где класс T является «перерабатывают на классе» по Splittable т.е. реализации класса.

Вы просто должны знать, что истинная сущность-типирование не поддерживается Java - то, что у вас есть рекурсивно связан параметр типа, который якобы, но не обязательно, само-типа. См. Мой ответ here для получения более подробной информации об этом шаблоне и его подводных камнях.

+0

Пол, Большое спасибо за подробное объяснение и предложения. Я видел многие из этих статических методов в отношении дженериков, и теперь я лучше понимаю, почему это так, и почему компилятор жалуется на цепочку .splitOff.append. Как вы можете себе представить, это ничего не изменит, но я думаю, что я переработаю этот раздел, чтобы проверить, могу ли я обеспечить проверку типов без предупреждений, если не более, чем для понимания. –

+0

Спасибо, Пол за отличные объяснения. – vikingsteve