【问题标题】:generic interface with parameterized method types具有参数化方法类型的通用接口
【发布时间】:2021-05-10 11:58:51
【问题描述】:

我正在尝试创建一个通用接口,因为这些方法几乎用于我拥有的所有服务中,但是当我尝试实现这些方法时,我得到的只是“方法不会覆盖其超类中的方法”。

@Service
public interface CustomInterface {

    <T, R> List<T> get(final R req);

    <T> T getById(final String id);

    <T, R> T create(final R req);

    <T, R> T update(final String id, final R req);

    <T, R> T patch(final String id, final R req);

    <T> T delete(final String id);

}
///// This is the implementation of one of the methods!
@Override
public CustomerResponseObject create(final CustomObject req) {
    return null;
}

我做错了什么?我真的认为这会奏效!

【问题讨论】:

  • 不确定为什么要在每种不同的方法上声明 T 和 R。似乎 T 和 R 应该是接口的泛型类型参数。即 CustomInterface
  • T 可以是接口类型,是的,但 R 可以是每个方法上的不同对象。所以 R 需要特定于方法

标签: java spring spring-boot generics interface


【解决方案1】:

您似乎误解了泛型。

至关重要的是,它们链接事物。它们是编译器想象的虚构(JVM 运行时不知道泛型是什么,不应用它们,不关心它们 - 而且大多数泛型实际上在编译期间完全消除了,它们不在类中文件),因此它们的唯一目的是让编译器帮助您并告知您可能在哪里出现输入错误。

因此,他们链接 - 他们让你对编译器说:“嘿,这个方法?它可以接受一些对象,它是一个类的实例,它是 Number 或其子类型。并且然后,它返回给定的相同类型”。它可以让您将返回类型链接到参数的类型,或者将两个参数类型链接在一起,或者它们可以将在您的类中显示为字段的列表的组件类型链接到类中其他地方的方法的参数等。

如果你的泛型类型只出现在一个地方,那就没用了

因此,您在示例中使用的每个类型参数都是完全无用的,因此我得出结论,您不了解该机制。

&lt;T, R&gt; T create(final R req); 表示如下:

对于任何给定调用此create 方法的调用,有一些类型我们将规定为 T 和一些类型我们将称为 R,仅用于此方法调用(与类型本身的泛型相比,它是每个实例,有效)。它们没有界限(&lt;T extends Number&gt; 被称为“界限”。&lt;T&gt; 表示它是无限的;任何类型都可以)。

返回类型为T,参数为R。

鉴于没有界限,这只是一种奇怪的写法:

Object create(Object req);

而你实现的不是那个。

&lt;T, R&gt; 在此构造中声明类型变量;它们的所有其他出现使用它们。鉴于您在这里为每个方法声明了它们,它们都是完全不相关的类型变量。是这样的:

void foo() {
    int x = 5;
}

void bar() {
    int x = 10;
}

这两个本地变量之间存在关系。它们都被命名为 x,这根本不会使它们相关。这同样适用于您的代码:每个方法都有自己独特的类型 var;它们都被命名为 T 或 R 的事实并没有使它们彼此相关。

大概,您打算为这个类型相关的自定义接口。

这意味着它需要是类型本身的属性:

public interface CustomerInterface<T, R> {
    List<T> get(R req);
    T getById(String id);
    T create(R req);
    // etc
}

现在所有这些 Ts 和 Rs 实际上指的是同一个事物,而现在你正在链接事物。然后你可以编写一个编译好的实现:

public class MyCustomImpl implements CustomerInterface<CustomObject, CustomerResponseObject> {
    public CustomerResponseObject create(final CustomObject req) {
      ..
    }
}

这会编译得很好。

注意:不要在接口方法“final”中使用参数 - 这是毫无意义的代码混乱。

【讨论】:

  • 好吧,我明白你的意思了,但是如果我对每种方法都有不同的参数对象怎么办。就像有 create(CustomCreateObject) 和 get(CustomGetObject)。
  • @WilssoN93 那么你的接口需要 5 个泛型类型参数。 1 表示实体类型,1 表示获取请求类型,1 表示创建请求类型,1 表示更新,1 表示补丁。你最终会得到一个像MyFooService implements CustomInterface&lt;Foo, FooGetRequest, FooCreateRequest, FooUpdateRequest, FooPatchRequest&gt; 这样的类。笨重?是的,但是当合约可以在 5 个不同的维度上变化时,您试图使 2 个合约相互兼容。
  • 是的,这看起来不太干净:p 我想我要放弃这个想法了:p
  • @WilssoN93 这有什么实际意义?你想“获得”什么,而不是仅仅拥有一个接受并返回 Object 的接口,或者完全摆脱这个接口?
【解决方案2】:

接口中使用的方法必须是实现原始接口代码的“类内部重写(每个接口方法签名的完整代码)”。 方法签名的接口定义充满了没有正文代码的抽象签名。 一个适当的类对象将完成可用的编码方法签名。 当编写类以实现接口时,类型的多态因素允许类实例被称为与接口的类型而不是类的类型相同,但是类实例既是类型又是从类的新实例变量实例化其接口类型的变量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多