我正在用Java编写一个方法:
List<Foo> computeFooList(/* arguments */)
{
/* snip */
}
我想用完全相同的逻辑编写第二种方法,但返回类型不同:
List<String> computeStringList(/* same arguments */)
{
/* snip */
}
我正试图找出一种非hackish方法来最小化两种方法之间重复代码的数量。两者之间的唯一逻辑差异在于,当将对象添加到返回的列表时,第一个方法会添加实际Foo
:
List<Foo> computeFooList(/* arguments */)
{
List<Foo> toReturn = ...
...
for (Foo foo : /* some other list of Foo */)
{
if (/* some condition */)
{
toReturn.add(foo);
}
}
...
return toReturn;
}
,第二个添加String
的{{1}}表示形式:
Foo
实际上, 简单。我不想向List<String> computeStringList(/* same arguments */)
{
List<String> toReturn = ...
...
for (Foo foo : /* some other list of Foo */)
{
if (/* some condition */)
{
toReturn.add(foo.toString());
}
}
...
}
添加Foo
,除非我完全确定它属于那里。因此,使用辅助函数每个toReturn
做出该决定。有两个不同版本的方法,我也需要不同版本的辅助函数 - 最后,我将编写两组近似相同的方法,但是对于一个小的泛型类型。
我可以编写一个包含所有决策逻辑的方法,但可以生成 foo
或List<Foo>
吗?是否可以在不使用原始List<String>
类型(泛型土地中的不良做法!)或通配符List
类型的情况下执行此操作?我想象一个看起来像这样的实现:
List<?>
这有什么好的,优雅的方法吗?我一直在玩泛型方法,但我看不到这样做的方法。即使用反射捣乱也没有把我带到任何地方(也许我需要TypeToken
之类的东西?...... eww)。
答案 0 :(得分:7)
您不能将转换逻辑外部化为单独的策略(例如Guava的Function<F, T>
):
public <T> List<T> computeList(/* arguments */, Function<? super Foo, T> f) {
List<T> toReturn = ... ...
for (Foo foo : /* some other list of Foo */) {
if (/* some condition */) {
toReturn.add(f.apply(foo));
}
}
return toReturn;
}
computeFooList:
computeList(..., Functions.identity());
computeStringList:
computeList(..., Functions.toStringFunction());
答案 1 :(得分:0)
我有一个“SearchFilter”接口和一个“FilterAdapter”抽象类,我以类似的方式使用它。决策逻辑可以独立地并以通用方式从实际添加到返回列表中。我会检查每个Foo并说“true”包含它或“false”排除它。
public interface SearchFilter<T>
{
public boolean include(T item);
public Collection<T> apply(Collection<T> items);
}
可以使用apply()
方法将过滤器应用于现有集合,并返回仅包含所需项目的新集合。
newCollection = myfilter.apply(originalItems);
这可能对您没有用,但include()
概念应该可以很好地避免重复决策逻辑。
您可以拥有FooFilter extends FilterAdapter<Foo>
(我也会在线匿名实例化这些内容),它提供了include
的实现
public FooFilter extends FilterAdapter<Foo>
{
public boolean include(Foo item)
{
if (item.isInvalid()) return false;
// or any other complex logic you want
return item.hasGoodThings();
}
}
apply()
方法几乎总是循环遍历初始集合并测试include
,因此它在我的FilterAdapter
中有一个默认实现,但可以被覆盖。
答案 2 :(得分:0)
这有点难看,但我认为这可能有用:
List<Foo> computeFooList(/* args */) {
return computeEitherList(/* args */, Foo.class);
}
List<String> computeStringList(/* args */) {
return computeEitherList(/* args */, String.class);
}
private <T> List<T> computeEitherList(/* args */, Class<T> whichType) {
List<T> rval = new ArrayList<T>();
for (Foo foo : listOfFoo) {
// process foo
if (whichType.equals(Foo.class)) {
rval.add(whichType.cast(foo));
}
else if (whichType.equals(String.class)) {
rval.add(whichType.cast(foo.toString()));
}
else {
throw new SomeException("Cannot compute list for type " + whichType);
}
}
return rval;
}