我有一个方法parse(String value)
的界面,它可能有不同的实现,它们会返回<String, Integer>
<String, String>
或其他任何地图。如何使这个通用足以让我扩展为不同的返回类型?
目前,我这样做:
public interface Parser <K,V> {
Map<K,V> parse(String document);
}
但是,这将使其仅适用于地图。有人能告诉我有没有办法让它对不同的返回类型具有通用性?
答案 0 :(得分:1)
如果你想在返回类型中使你的界面通用,我会建议延长JoeC的评论。
从Java 8开始,有java.util.function
-package,为基本转换提供接口。特别是,界面Function
可用于满足您的目的。我会建议这样的实现:
// file: Parser.java
import java.util.function.Function;
public abstract class Parser<R> implements Function<String, R> {
@Override
public final R apply(String document) {
return (this.parse(document));
}
abstract public R parse(String document);
}
上述示例的实例化如下所示:
String document = ...;
Parser<Map<K, V>> mapParser = ...; // instantiate a fitting Parser
Map<K, V> result = mapParser.parse(document);
(鉴于K
和V
是此代码块中已知的通用参数。)
您可以进一步指定接口以获得更简单的语法:
// file: MapParser.java
import java.util.Map;
public abstract class MapParser<K, V> extends Parser<Map<K, V>> {}
使用此(空)接口,您可以将上述代码重新编写为:
String document = ...;
MapParser<K, V> mapParser = ...; // instantiate a fitting MapParser
Map<K, V> result = mapParser.parse(document);
正如@matoni所提到的,可以编写接口IParser
和IMapParser
,并在它们之上设置抽象类Parser
和MapParser
:
// file: IParser.java:
import java.util.function.Function;
public interface IParser<R> extends Function<String,R> {
@Override
default public R apply(String document) {
return (this.parse(document));
}
public R parse(String document);
}
// file: IMapParser.java:
import java.util.Map;
public interface IMapParser<K, V> extends IParser<Map<K, V>> {}
// file: Parser.java:
public abstract class Parser<R> implements IParser<R> {
@Override
public final R apply(String document) {
return (this.parse(document));
}
}
// file: MapParser.java:
import java.util.Map;
public abstract class MapParser<K, V> extends Parser<Map<K, V>>
implements IMapParser<K, V> {}
接口为用户提供了更大的灵活性,因为一个class
可以实现多个interface
,但只有extends
一个class
。{}但是,在缺点方面,接口IParser
和IMapParser
的开发人员无法强制执行该方法apply(...)
无法覆盖。因此,从理论上讲,Parser
的实施者可能会以不同方式实施apply(...)
和parse(...)
,这可能会导致意外行为。使用抽象类Parser
和MapParser
时,开发人员会强制执行apply(...)
调用parse(...)
,从而实现一致行为。
答案 1 :(得分:0)
如果您希望它返回任何类型,只需使用一个通用类型定义它,如T:
public interface Parser <T> {
<T> parse(String document);
}
这是可能的,但我担心你以后会遇到新的挑战。 Java目前已经可以从泛型类型实例化一个类,因此您还必须将该类类型作为参数传递:
public interface Parser <T> {
<T> parse(Class<T> clazz, String document);
}
你可以做到这一点,但我认为应该进一步设计你的架构。如果来自文档的返回类型可以是任何东西,在大多数情况下,这是一种设计薄弱的气味,并将导致意大利面条代码。
答案 2 :(得分:0)
评论已经给了你很好的提示,但我想你需要一个例子。
// imports elided
interface Parser<T> {
T parse(String document);
Parser<Map<String, Integer>> static mapParser() {
// replace with actual parsing code
return document -> {
Map<String, Integer> result = new Hashmap<>();
result.put(document, document.length());
return result;
}
Parser<List<String>> static listParser() {
return document -> Collections.singletonList(document);
}
}
请注意,实现只是占位符 - 它们只是为了说明您可以创建的Parser类型。我还使用了一个更简洁的lambda,因为你的接口只有一个实例方法parse(String document)
,它将它限定为FunctionalInterface
,允许你在实现命名接口方法时替换匿名lambda表达式。
然后,来电者可以通过以下方式致电:
String document = "abc";
Map<String, Integer> lookup = Parser.mapParser().parse(document);
List<String> list = Parser.listParser().parse(document);