接口方法的通用返回类型

时间:2017-09-14 19:52:00

标签: java generics interface java-8 return

我有一个方法parse(String value)的界面,它可能有不同的实现,它们会返回<String, Integer> <String, String>或其他任何地图。如何使这个通用足以让我扩展为不同的返回类型?

目前,我这样做:

public interface Parser <K,V> { 

    Map<K,V> parse(String document);
}

但是,这将使其仅适用于地图。有人能告诉我有没有办法让它对不同的返回类型具有通用性?

3 个答案:

答案 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);

(鉴于KV是此代码块中已知的通用参数。)

您可以进一步指定接口以获得更简单的语法:

// 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所提到的,可以编写接口IParserIMapParser,并在它们之上设置抽象类ParserMapParser

// 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。{}但是,在缺点方面,接口IParserIMapParser的开发人员无法强制执行该方法apply(...)无法覆盖。因此,从理论上讲,Parser的实施者可能会以不同方式实施apply(...)parse(...),这可能会导致意外行为。使用抽象类ParserMapParser时,开发人员会强制执行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);
相关问题