0

我对 Java 和泛型比较陌生。我试图了解我在编写通用方法时是否做错了什么。我有以下代码(大大简化):

public class ContentIniter {       
    public ContentType getContentType();
}

public interface Content {
}

public class Show implements Content {       
}

public class Movie implements Content {       
}

public enum ContentType {
    Movie, Show
}

public class Channel {

    public List<Show> getShows() {
        return getContentByType(ContentType.Show)
    }

    public List<Movie> getMovies() {
        return getContentByType(ContentType.Movie)
    }

    private <T> List<T> getContentByType(ContentType contentType) {
        List<T> typeContents = Lists.newArrayList();
        List<ContentIniter> allContentIniters = someMethod(); // Returns initers for both shows and movies
        for (Content contentIniter : allContentIniters) {
            if (contentIniter.getContentType().equals(contentType)) {
                switch (contentType) {
                case Movie:
                    typeContents.add((T) new Movie(contentIniter));
                    break;
                case Show:
                    typeContents.add((T) new Show(contentIniter));
                    break;
                }
            }
        }
        return typeContents;
    }

}

我的问题与这条线有关:

typeContents.add((T) new Movie(contentIniter));

我能够编译代码的唯一方法是如果我将内容对象转换为 T。但这对我来说似乎很糟糕(而且我不明白为什么编译器不能根据调用推断类型)。此外,即使代码有效,IntelliJ 也会抱怨未经检查的演员表。

有没有更好的方法来编写泛型方法?

更新:当我试图简化代码时,它有点搞砸了。修复了对typeContents. 另外,我增加了一些复杂性,以便更好地反映现实,希望能解释为什么我不只是检查instanceof.

更新 2:意识到还有另一个错误......ContentIniter没有实现内容。还值得注意的是,ContentIniter它只是一个虚构的对象。如果看起来很奇怪,可以将其视为 Content 对象用来委托某些行为的事件或其他策略。

4

4 回答 4

1

您没有正确使用泛型,而是在确实没有必要时将它们与枚举混合在一起。理想情况下,您会调用getContentByType<Show>()然后allContents使用反射确定正确类型的列表。

尝试更多类似(未经测试)的东西:

private <T> List<T> getContents() {
    List<T> typeContents = Lists.newArrayList();
    List<Content> allContents = someMethod(); // Returns both shows and movies
    for (Content content : allContents) {
        if (content instanceof T) {
            typeContents.add((T) content);
        }
    }
    return typeContents;
}

并调用:

List<Show> shows = getContents<Show>();

然后,您可以将对其调用的类型限制为仅扩展的类型Content

private <T extends Content> List<T> getContents() {
    ...
}
于 2013-07-11T04:38:38.853 回答
0

在这里使用枚举似乎很奇怪,并且您失去使用泛型的优势的方式。

初始事物使事情变得更加奇怪和混乱。

它可能看起来更自然,如下所示:

public interface Content {
}

public class Show implements Content {       
}

public class Movie implements Content {       
}

//......
    private <T extends Content> List<T> getContentByType(Class<T> contentType) {
        List<T> result = Lists.newArrayList();

        List<Content> allContents = someMethod();   // ContentIniter is just a mess
                                                    // Get all content you have!
        for (Content content: contents) {
            if (contentType.isAssignableFrom(content.getClass())) {
                result.add(content);
            }
        }
        return result;
    }

使用方法是

List<Show> result = channel.getContent(Show.class);
于 2013-07-12T04:52:13.403 回答
0

代码示例更改后,我删除了原始答案。

我真的不认为你可以避免演员阵容和@SuppressWarnings("unchecked").

只要您知道自己在做什么,这可能是最好的解决方案。

另一种方法是不使用该方法,而在and方法getByContentType上有一些重复的逻辑。getShows()getMovies()

例如:

public List<Show> getShows() {
    List<Show> shows = new ArrayList<Show>();

    List<ContentIniter> allContentIniters = someMethod();

    for(ContentIniter initer: allContentIniters) {
        if(initer.getContentType().equals(ContentType.Show)) {
            shows.add(new Show(initer));
        }
    }

    return shows;
}

public List<Movie> getMovies() {
    List<Movie> movies = new ArrayList<Movie>();

    List<ContentIniter> allContentIniters = someMethod();

    for(ContentIniter initer: allContentIniters) {
        if(initer.getContentType().equals(ContentType.Movie)) {
            movies.add(new Movie(initer));
        }
    }

    return movies;
}
于 2013-07-11T05:22:30.383 回答
0

实际上答案比你想象的要简单:你只需要检查你的 Content 实例是 Show 还是 Movie 来让你的编译器满意:

if (content instanceof Movie)
    contents.add((Movie) content);
if (content instanceof Show)
    contents.add((Show) content);

无论如何,我会说您编写通用方法的方式是正确的。但是由于有一种本地方法可以检查实例的类型 ( instanceof),所以您应该使用它:)

编辑:我仍然认为你应该使用instanceof.

另外,您应该使用 aList<Content>而不是 a List<ContentIniter>,因为 Content 是一种更全局的类型:如果有人提出 Content 的另一个实现,他将不必更改您的代码。实际上,例如,当您使用接口列表而不是 ArrayList 时,您正在做同样的事情,因为 List 不如 ArrayList 特定。

此外,使用枚举并不是一个错误:如果你想使用一个,你可以。但它不应该用于确定实例的类型。实例的类型包含在实例本身中,句号。不过,我会说 Daniel Imms 的解决方案比我的更优雅,并且更好地利用了 Java 类型的特性。

public interface Content {
    public STContentType getContentType();
}

public class ContentIniter implements Content {       
}

// You can keep the enum, as long as it's not used 
// to check for the type of an instance of ContentIniter
public enum ContentType {
    Movie, Show
}

public class Show implements Content {       
}

public class Movie implements Content {       
}


public class Channel {

    public List<Show> getShows() {
        return getContentByType(ContentType.Show)
    }

    public List<Movie> getMovies() {
        return getContentByType(ContentType.Movie)
    }

    private <T> List<T> getContentByType(ContentType contentType) {
        List<T> typeContents = Lists.newArrayList();
        // Using more generic type Content
        List<Content> allContentIniters = someMethod(); // Returns initers for both shows and movies
        for (Content contentIniter : allContentIniters) {
            // If it's a Show and I asked for Shows
            if (contentIniter instanceof Show && contentType == ContentType.Show)) {
                typeContents.add(contentIniter);
            }
            // If it's a Movie and I asked for Movies
            if (contentIniter instanceof Movie && contentType == ContentType.Movie){
                typeContents.add(contentIniter);
            } 
        }
        return typeContents;
    }

}
于 2013-07-11T04:35:29.590 回答