Java returning subclass without generic parameter in superclass method

1k views Asked by At

I really love using initializers to make constructing and configuring classes more understandable. Basically I prefer this:

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

Rather than this:

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

This is a bit of a pain in Java when mixed with inheritance, as returning types default to as generic as possible. I've found a workable solution using self-referential inheritance (Java: returning subclass in superclass method signature), but I'm having some problems with it.

My issue is that the Parent class implements Iterable, but the generic type parameter gets lost, so the for-each loop wants to return an Object instead of a File.

Here's an SSCCE showing the behavior:

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);
  }
}

What's going on, and how can I fix it?

1

There are 1 answers

3
Morgen On BEST ANSWER

So it turns out the problem was that I was not specifying the type correctly. A minimal fix in the PrintWords function solves the problem.

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