Sam Douglas
Sam Douglas

Reputation: 3

Nested generic collections

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?

Upvotes: 0

Views: 70

Answers (3)

Eric Jablow
Eric Jablow

Reputation: 7899

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.

Upvotes: 0

zw324
zw324

Reputation: 27210

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.

Upvotes: 1

rgettman
rgettman

Reputation: 178333

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.

Upvotes: 3

Related Questions