j0ntech
j0ntech

Reputation: 1168

Casting objects in list using generics

I'm having problems using generics and lists with interfaces.

I have an interface Comment, which is extended by the class CommentNews.

Comment:

public interface Comment {

    public static final String COMMENT_NEWS_PATH = "/comment/news";
    public static final String COMMENT_EVENT_PATH = "/comment/event";
    public static final String COMMENT_GROUP_PATH = "/comment/group";

    public User getUser();

    public void setUser(User user);

    public String getText();

    public void setText(String text);

    public Date getModified();

    public void setModified(Date modified);

    public void setCommentsList(List<?> commentsList);

    public <T extends Comment> List<T> getCommentsList();
}

CommentNews:

public class CommentNews implements Comment {

    private Integer id;
    private User user;
    private News news;
    private String text;
    private List<CommentNews> commentsList;

    public CommentNews() {}

    // Methods snipped for brevity

    public List<CommentNews> getCommentsList() {
        return commentsList;

    }

    public void setCommentsList(List<?> commentsList) {
        this.commentsList = (List<CommentNews>) commentsList;

    }
}

The problem is with the setCommentsList method, the class cast doesn't actually cast each object in the list. I can't change the methods signature as that will generate a name clash with the interface.

Is there a way to make the casting using generics? I would like to refrain from iterating through the list and casting each object manually.

UPDATE: If I change the Comment interface to If I change the interface to

public <T extends Comment>void setCommentsList(List<T> commentsList);

and CommentNews class to

public void setCommentsList(List<CommentNews> commentsList) {
        this.commentsList = commentsList;   
    }

it should be typesafe, but this results in a name clash between the interface and class.

Upvotes: 0

Views: 901

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500105

Fundamentally, your interface isn't type-safe. There's nothing to stop someone from doing something like this:

Comment comment = ...; // Wherever
List<Integer> numbers = new List<Integer>();
numbers.add(10);
comment.setCommentsList(numbers);

Do you really want to allow that?

You could have a list wrapper which performs the cast whenever it's accessed (rather than up-front) but it would be better to change the interface.

EDIT: If you want any kind of comment to only hold sub-comments of the same kind, you probably want to make your Comment interface generic, like this:

public interface Comment<T extends Comment<T>> {

    // Note: no public modifier; it's allowed by the spec but discouraged
    void setCommentList(List<T> comments);
    List<T> getCommentList();
}

(You could still use wildcards if you wanted to, but you may well not need to, and it will complicate things.)

Upvotes: 3

Related Questions