0

Without getting bogged down with specifics, my code represents a library whereby each book is made up of a Set of pages containing a Set of Words.

I have created my own Set implementations:

class PageSet<E> extends HashSet<E>(){
    public boolean set(int index, E e){....}
    ....
}

and

class WordSet<E> extends HashSet<E>(){
    public boolean set(int index, E e){....}
    ....
}

I've got stuck when I try to create a Book in my main class:

Set<Set<Word>> dictionary = new PageSet<WordSet<Word>>();

Which results in a type conversion mismatch. However it will quite happily accept

Set<Set<Word>> dictionary = new PageSet<Set<Word>>();

Could someone please shed some light as to what I'm doing wrong when using a generic setup like this?

  • Not related to your question: I am confused about the index in your `set` methods, but then again, I don't know what your application does. Couldn't you just use a List instead of a Set where the index of the list would be the index? Or a Map? – toto2 May 17 '13 at 20:19

3 Answers3

3

Basically, a PageSet<WordSet<Word>> is not a Set<Set<Word>>, because X<Subclass> is not a X<Superclass>.

If you had said

Set<WordSet<Word>> dictionary = new PageSet<WordSet<Word>>();

then that would have worked also.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • Thanks, makes sense. Just as a followup, if I wanted to override a method that returns Set> (eg: gets the dictionary) then it won't be able to convert Set> to Set> – Sam Douglas May 17 '13 at 20:23
1

It's either

Set<Set<Word>> dictionary = new PageSet<Set<Word>>();

or

Set<WordSet<Word>> dictionary = new PageSet<WordSet<Word>>();

Since although WordSet is a subclass of Set, a Set<WordSet> is not a subclass of Set<Set>.

In other words, generics are not covariant, which is different from things like arrays.

Community
  • 1
  • 1
zw324
  • 26,764
  • 16
  • 85
  • 118
0

In any case, you should not extend collections unless you are trying to create new collection types. Since you cannot restrict the visibilities of superclass methods in a subclass, people will be able to write

WordSet<Word> words = ...;
words.clear();

You probably do not want to give clients that power. Instead, use aggregation instead of inheritance.

class Word {
    private String text;
    private PartOfSpeech part;
    // Constructors, getters, setters, equals, hashCode are elided.
}

class Page {
    private int pageNumber;
    private Set<Word> contents = new HashSet<>();

public class Book {
    private String title;
    private List<Page> pages = new ArrayList<>();
}

Pages in a book are ordered linearly, which is why I used lists. I'm not sure why you used sets. But in any case, by encapsulating the collections inside the classes, you can provide client code exactly the interface you want them to use. The visibilities were chosen deliberately; this looks like a cluster of related classes, but you might want to change them.

Eric Jablow
  • 7,874
  • 2
  • 22
  • 29