【问题标题】:Redundant generic parameters冗余通用参数
【发布时间】:2012-03-13 22:41:37
【问题描述】:

我有这两个接口和类:

public interface Identifiable<T> {
    T getId();
}

public interface GenericRepository<T extends Identifiable<K>, K> {
    T get(K id);
}

public class MyEntity implements Identifiable<Long> {

    private Long id;

    public Long getId() {
        return id;
    }
}

public class MyService {
    private GenericRepository<MyEntity, Long> myEntityRepository;
}

这一切都按预期工作。但在我看来,GenericRepository (K) 中的第二个通用参数是多余的。因为我知道 MyEntity 是一个 Identifiable,所以我认为如果我最终可以像这样使用它会很棒:

public class MyService {
    private GenericRepository<MyEntity> myEntityRepository;
}

但我正在尝试不同的事情但没有成功。可能吗?如果没有,为什么不呢?

更新:回答一些回复。我认为编译器知道 MyEntity 中的泛型类型。例如:

public class MyEntityGenericRepository implements GenericRepository<MyEntity, Long> {
    // compiles...
}

public class MyEntityGenericRepository implements GenericRepository<MyEntity, String> {
    // compiler says: "Bound mismatch: The type MyEntity is not a valid substitute for the bounded parameter <T extends Identifiable<K>> of the type GenericRepository<T,K>"
}

【问题讨论】:

    标签: java generics


    【解决方案1】:

    我认为你不能省略它。对于T extends Identifiable&lt;K&gt;,您想说泛型类型参数必须是Identifiable。由于Identifiable 是一个泛型类,你也需要提及它的泛型类型参数(如果你想遵守规则——如果你省略它,你将失去GenericRepository 的所有泛型类型安全性,因为向后兼容规则)。另请注意,K 实际上用作GenericRepository.get 的参数类型。并且由于该类型可能与T 不同,因此您需要通过将其声明为GenericRepository 的另一个泛型类型参数来满足编译器的要求。否则编译器无法知道K 是什么。

    【讨论】:

    • @sinuhepop,编译器当然可以检查K的实际类型是否与Identifiable&lt;K&gt;中的类型参数匹配。尽管如此,您仍需要声明 K 为泛型类型参数(因此,在实例化点传递其实际值)。这就是语言的工作方式。你是对的,理论上编译器可以推断出这些知识,但现在还不行,而且很多年……如果有的话(不幸的是,Java 的开发进展非常缓慢慢慢地朝那个方向……)。
    【解决方案2】:

    从 GenericRepository 类的角度来看,这不是多余的。当它有像T get(K id) 这样的方法时,它不知道它可以接受哪些类型的id 参数。你可以这样写:

    interface GenericRepository<T extends Identifiable<?>> {
        T get(Object id);
    }
    

    现在您不必将Long 写为类型参数,但是您无法在编译时检查get 方法是否正确使用。所以类型变量有特定的用途。

    对于字段声明,当你有一个泛型类型时,你必须指定它使用的所有类型变量。当然,您可以争辩说,如果语言能够理解其中一个参数值可以从另一个参数值推断出来,那就太好了,但这是值得商榷的。

    【讨论】:

    • 请看我的版本。似乎编译器可以以某种方式推断类型。
    • @sinuhepop,不,它不是在那里推断任何类型,只是比较两个具体定义的类型参数的相等性。 (一个简单的例子)类型推断是this(在Java 7中实现)。
    【解决方案3】:

    除了引入一个只优化 GenericRepository 的接口之外,你无能为力

      public interface LongKeyedRepository<T extends Identifiable<Long>> 
            extends GenericRepository<T, Long> { {
      //No new methods need to be defined
      }
    

    那么你就可以拥有了

    private LongKeyedRepository<MyEntity> myEntityRepository;
    

    等等

    【讨论】:

    • 谢谢。目前我正在做类似的事情(我已经稍微简化了我的结构)。但是我不太喜欢它,我不明白为什么需要它。
    【解决方案4】:

    如果我没记错的话,泛型将全部编译为就好像它们只是 Object。现在(硬)检查语法以确保您没有将苹果和橙子放在一起,因为泛型是在 Java 的初始设计之后添加的。这就是为什么泛型如此受限的原因......

    【讨论】:

      【解决方案5】:

      它适用于(即编译)

      public interface GenericRepository<T extends Identifiable> {
          T get(T id);
      }
      

      但它仍然说 Identifiable 是一种原始类型,并且应该对其进行参数化。

      希望对你有帮助。

      【讨论】:

      • 这不太一样——你已经声明get 接受T 类型的参数,而不是K。可以说这就是问题的症结所在,如何在类中拥有K 引用而不必在构造时声明它,所以回避它有点厚颜无耻。 :)
      • 我会说在类中没有 K 引用而不必在构造时声明它是不可能的。我能想到的唯一另一件事是公共接口 GenericRepository> { T get(T id); }
      • 是的,这就是问题:“有可能吗?如果没有,为什么不呢?
      • 因为在 Java 中你不能使用没有声明的东西。请注意,如果您尝试 public interface GenericRepository> { T get(T id); } 它不会编译
      【解决方案6】:

      为什么不能从中删除第二个 K,

      public interface GenericRepository<T extends Identifiable<K>, K> {
      

      那么,与其像上面那样,我们可以把它当作

      public interface GenericRepository<T extends Identifiable<K>> {
      

      这样我们就可以做你想做的事了。

      【讨论】:

      • 是的,但不幸的是编译器说“K无法解析为类型”。
      猜你喜欢
      • 2012-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-12
      • 1970-01-01
      相关资源
      最近更新 更多