2014-11-11 2 views
-1

Мне очень нравится использовать инициализаторы, чтобы сделать сборку и настройку классов более понятными. В основном я предпочитаю это:Ядро, возвращающее Java подкласс без общего параметра в методе суперкласса

new ClassObj().StartingFrom(1).EndingAt(5).IncrementBy(1).Backwards(); 

Вместо этого:

new ClassObj(1,5,1,false); 

Это немного боль в Java при смешивании с наследованием, как возвращение типов по умолчанию как родовое, насколько это возможно. Я нашел работоспособное решение с использованием самореференциального наследования (Java: returning subclass in superclass method signature), но у меня возникли проблемы с ним.

Моя проблема в том, что класс родителя реализует Iterable, но параметр родового типа теряется, поэтому цикл for-each хочет вернуть объект вместо файла.

Вот SSCCE показывает поведение:

public class SSCCE { 
    private static abstract class Sentence<T extends Sentence<T>> implements Iterable<String> { 
    protected LinkedList<String> Words = new LinkedList<>(); 

    abstract T self(); 

    public T Say(String word) { 
     Words.add(word); 
     return self(); 
    } 

    @Override 
    public Iterator<String> iterator() { 
     return Words.iterator(); 
    } 
    } 

    static class QuietSentence extends Sentence<QuietSentence> { 
    public QuietSentence Whisper(String word) { 
     Say(word.toLowerCase()); 
     return this; 
    } 

    @Override 
    QuietSentence self() { 
     return this; 
    } 
    } 

    static class LoudSentence extends Sentence<LoudSentence> { 
    public LoudSentence Shout(String word) { 
     return Say(word.toUpperCase()); 
    } 

    @Override 
    LoudSentence self() { 
     return this; 
    } 
    } 

    static void PrintWords(Sentence words) { 
    for(Object obj : words) { 
     // I'd really like to avoid this cast 
     String word = (String)obj; 
     System.out.println(new StringBuilder(word).append(": ").append(word.length()) 
        .toString()); 
    } 
    } 

    public static void main (String[] args) { 
    QuietSentence peaceful_words = new QuietSentence().Say("Hello").Whisper("World"); 
    PrintWords(peaceful_words); 

    LoudSentence noisy_words = new LoudSentence().Say("Hello").Shout("World"); 
    PrintWords(noisy_words); 
    } 
} 

Что происходит, и как я могу это исправить?

+0

Вы знаете, что '(T) это небезопасный литье, не так ли? – newacct

+0

Обычно это было бы. В этом случае 'T', как известно, является либо типом' Sentence', либо одним из его подклассов, поэтому безопасно делать трансляцию. – Morgen

+0

Nope. Это небезопасно. да, 'T', как известно, является подтипом' Sentence ', но' Sentence ', как известно, не является подтипом' T'. – newacct

ответ

1

Так получилось, проблема в том, что я не задавал тип правильно. Проблема минимальна в функции PrintWords решает проблему.

static void PrintWords(Sentence<? extends Sentence> words) { 
    for(String word : words) { 
     System.out.println(new StringBuilder(word).append(": ").append(word.length()) 
       .toString()); 
    } 
} 
+0

Я проголосовал за вас, я писал, что вы пропустили наследование – eduyayo

+0

'Sentence ' было бы более правильным. В противном случае у вас есть сырой тип. Но да, это в основном решение, потому что использование raw 'Sentence' приводит к стиранию всего класса (включая тип' Iterable'). – Radiodef

+0

В этом случае я считаю, что ' 'является более правильным, потому что он сохраняет безопасность типов. Более конкретный родовой ограничивает ссылки на «Предложение» и его подтипы. – Morgen

Смежные вопросы