我扩展了一个在几个类中使用泛型的现有项目。
我正在开发一个名为PhiFunction的类,这个类有一个接受许多参数的construcor。这些包括两个我想强制为相同类型的参数,E。这些参数仅在构造函数中使用,它们不存储为类字段。
我不想将这种类型添加到类签名中(我希望尽可能简化类定义)。类签名目前只包含类型T.为了使类签名尽可能简单,我将此类型的定义添加到构造函数签名中,如下所示:
简化旧代码:
public class PhiFunction<T> {
...
public PhiFunction(
final MathematicalGroup<?> group, final List<?> baseElements, ...) {
简化新代码:
public class PhiFunction<T> {
...
public <E extends GroupElement<T>> PhiFunction(
final MathematicalGroup<E> group, final List<E> baseElements, ...) {
代码编译良好,工作正常。
现在令我惊讶和困惑的是,仍然可以像以前一样创建PhiFunction的实例。换句话说,在构造函数签名中添加此类型定义不会更改构造函数的使用方式(假设用户确实提供了使用相同类型的参数)。用户仍然可以像以前一样创建PhiFunction的实例,而不必担心这个类型定义被添加到构造函数中。我希望这个构造函数的用户必须将E定义为某个特定类,但他们不会。
之前我没有以这种方式使用泛型。似乎将类型定义添加到构造函数(或者我猜想的任何方法)只是允许定义一个类型,而不需要在构造函数的用户身上设置定义该类型的责任。
我想我的问题是,如果构造函数的签名包含泛型类型的规范,那么对构造函数的用户有什么额外的责任呢?
我还有一个相关的问题。是否应将此类型E作为参数添加到Java文档中?如何?我确信它应该被记录,因为构造函数的两个参数必须是该类型,但我不确定如何将它添加到Javadocs。
答案 0 :(得分:1)
您无需为调用泛型方法或构造函数执行任何额外的工作。大多数情况下,compiler will be able to infer the type argument基于您传递给方法的参数,或者来自返回类型(尽管不适用于此)。
所以,当你像这样创建那个类的实例时:
MathematicalGroup<Sometype> mathematicalGroup;
List<Sometype> list;
PhiFunction<Double> phiFunction = new PhiFunction<>(mathematicalGroup, list);
...构造函数的类型参数E
将自动推断为Sometype
。
但是,如果传递的参数不符合parmeter类型的规则,您将看到编译器错误,如下例所示:
MathematicalGroup<Sometype> mathematicalGroup;
List<SomeOthertype> list;
// This will give a compiler error.
PhiFunction<Double> phiFunction = new PhiFunction<>(mathematicalGroup, list);
如果有时类型推断无法按预期工作,您还可以选择提供显式类型参数:
// Redundant usage of explicit type argument
PhiFunction<Double> phiFunction =
new <Sometype>PhiFunction<Double>(mathematicalGroup, list);
虽然显式类型参数的上述用法是多余的,但是在某些情况下编译器不会推断出您期望它的类型。当您将不一致的参数传递给具有相同类型参数的参数时,就会出现这种情况。例如,考虑以下方法:
public static <T> void fill( T [] array, T elem) {
for (int i=0; i<array.length; ++i) { array[i] = elem; }
}
..如果您尝试将该方法调用为:
fill(new String [5], new String ("XYZ")); // This is fine
fill(new String [5], new Integer (100)); // This is also fine? How?
理想情况下,您希望第二个方法调用失败,因为String
和Integer
不应该替代相同的类型参数T
。但令人惊讶的是。编译器将type参数推断为您传递的所有超类型参数的交集。因此,类型T
被推断为:
T:=Object&Serializable&Comparable
在这种情况下,您可能希望将<Object>
作为显式类型参数:
YourClass.<Object>fill(new String[5], new Integer(100));
P.S :您是否知道you can invoke non-generic methods in generic way?嗯,仿制药充满惊喜:)
至于Javadoc,您不需要提供有关E
代表什么的任何信息。只需解释一下参数group
和list
的含义。而已。类型参数和形式参数无论如何都是方法签名的一部分,并且已经存在。考虑例如Arrays.binarySearch()
方法。
答案 1 :(得分:0)
将其添加到Javadoc:
@param <E> This represents...
此构造函数
public PhiFunction(
final MathematicalGroup<?> group, final List<?> baseElements, ...) {
允许将任何类型传递给MathematicalGroup
使用。 MathematicalGroup
是否已将自己的泛型与之相关联?
如果是这样,那么也许你的新构造函数是多余的。也就是说,即使原始构造函数具有<?>
,MathematicalGroup
类本身的泛型限制也已经执行了此操作。
public <E extends GroupElement<T>> PhiFunction(
final MathematicalGroup<E> group, final List<E> baseElements, ...) {
否则,这确实更具限制性,因为原始签名允许任何类型,并且此签名仅允许GroupElement<T>
或其子类之一。
就用户的使用方式而言:
MathematicalGroup mg = new <GroupElement<Object>>MathematicalGroup<Object>(groupElementInstance, ...);
这是一个奇怪的用例。通常,这种泛型传递是通过静态最终效用函数完成的。