Java 抽象方法中使用通配符和声明泛型的区别
Difference between using wildcards and declaring generic type in abstract method in Java
我试图理解 Java 中的泛型类型,理论上它看起来可以理解,但是当我需要将它应用到实际代码时我遇到了问题。我想声明将 return 泛型类型的抽象方法。假设我有一些名为 Magicable 的空接口,并且 2 class 实现了它:Magican 和 。
现在我想知道这 3 个声明之间有什么区别:
/*1*/protected abstract <T extends Magicable> List<T> getMagicables();
/*2*/protected abstract List<? extends Magicable> getMagicables();
/*3*/protected abstract List<Magicable> getMagicables();
在第一种情况下,当我想在扩展抽象 class:
的某些 class 中实现此方法的主体时遇到问题
@Override
protected List<Magican> getMagicable() {..}
我有警告消息:
Type safety: The return type List<Magican> for getMagicable() from the type MagicanService needs unchecked conversion to conform to List<Magicable> from the type MagicableService.
在第二种情况下我没有这个警告,但是我在抽象 class 中有问题,我在上面声明了抽象方法:
public void <T extends Magicable> T getOneFromList() {
List<T> list = getMagicables();
//.....
}
在这种情况下,我在 getMagicables() 调用中出现编译错误:
Type mismatch: cannot convert from List<capture#2-of ? extends Magicable> to List<T>
第三种情况导致上述两处代码编译错误。我不认为它是否适合我的情况。
对于问题的部分,您确实向我们展示了方法 /* 3 */ 就足够了,您的那部分代码不需要泛型。但你需要尊重可替代性:
你在 #1 中得到错误,因为子类型方法限制了 return 类型的范围:Magican
是 Magicable
但反之则不然。子类型中允许 Magicable
的超类型。子类型方法必须可以替代超类型方法,而在您的示例中不是这种情况。
#2 中的错误是由于通配符 ?
的性质造成的:? extends Magicable
和 T extends Magicable
不必是同一类型。如果 T
在 class 范围内声明,例如class Magican<T> implements Magicable<T>
(当然,在这种情况下,您的接口需要声明 T)在您的类型中出现的所有 T
都将引用相同的 class.
public abstract class AbstractMagicable<T extends Magicable> {
abstract List<T> getMagicables1();
abstract List<? extends Magicable> getMagicables2();
abstract List<Magicable> getMagicables3();
}
class MagicableWitch extends AbstractMagicable<Witch> {
@Override
List<Witch> getMagicables1() {
return null;
}
@Override
List<? extends Magicable> getMagicables2() {
return getMagicables1();
}
@Override
List<Magicable> getMagicables3() {
return Collections.singletonList(new Witch());
}
}
class MagicableMagician extends AbstractMagicable<Magician> {
@Override
List<Magician> getMagicables1() {
return null;
}
@Override
List<? extends Magicable> getMagicables2() {
return getMagicables1();
}
@Override
List<Magicable> getMagicables3() {
return Collections.singletonList(new Magician());
}
}
1) 当你想在使用它时用真实姓名替换它时使用T。例如 class MagicableWitch extends AbstractMagicable<Witch>
。
此处 Witch
已替换 T,因此 abstract List<T> getMagicables1();
在其具体 class.
中更改为 List<Witch> getMagicables1()
2)? class 将在运行时可用时使用 class 将被替换。
3) List<Magicable>
和 List<Witch>
是不同的,尽管 Witch implements Magicable
。 getMagicables3
中显示了实现
.
在您的第一种情况下,抽象方法被声明为使用通用类型 <T extends Magicable>
,这意味着您的方法可以 return Magicable 列表或实现它的任何类型。在您的实现中,您正在 return 使用具体类型 Magican,它是一个 Magicable。您可以安全地忽略警告并添加 @SuppressWarning("unchecked")
以禁用警告。需要注意的是,任何扩展您的 class 的 classes 都将被限制为 return 仅魔法师列表。
在第二种情况下,声明 List<T> list = getMagicables();
会抛出错误,因为您的方法不是 return 一个 List<T>
而是一个 List<? extends Magicable'
,这不是一回事.由于泛型的工作方式,当您声明使用未绑定通配符的 return 类型时,任何调用您的方法的代码都必须具有可接受的匹配类型,在您的情况下如 List<? extends Magicable>
或 List<?>
.
关于第三种情况,您的抽象方法 return 是 List<Magicable>
而您的实现 return 是 List<Magic>
。这似乎违反直觉,但您不能在 Java: List<Magicable> list = ArrayList<Magic>
中对泛型执行类似的操作。这可能看起来很奇怪,因为数组允许您声明类似 Magicable[] magics = new Magican[3];
的东西。这是一个常见的误解,因为数组是协变的,而泛型是不变的。协变的意思是,如果你有两个 classes Super
和 Sub extends Super
,Sub[] is a subtype of Super[]
。对于泛型,因为它们是不变的,所以两者之间没有关系,List<Sub>
不是 List<Super>
的子类型。
如果你想 return 泛型类型,只需在 class 扩展你的抽象 class 中使用与第一个案例 protected <T extends Magicable> List<T> getMagicable()
相同的类型声明.在 returned 类型中使用通配符是一个非常糟糕的主意,因为您强制 class 用户在他们的 List 变量声明中使用通配符。
- First case
只需声明您的方法:
@Override
protected <T extends Magicable> List<T> getMagicables() {
List<T> list = ...
return list
}
如果你真的想要这个:
@Override
protected List<Magican> getMagicable() {..}
您可能必须将通用 T 声明到 class 定义中
public abstract class AbstractKlass<T extends Magicable> {
protected abstract List<T> getMagicables();
}
然后在你的 Subclass:
public class MySubClass extends AbstractKlass<Magican> {
@Override
protected List<Magican> getMagicables() {
...
}
}
- Second case
编译错误是正常的,因为 <? extends Magicable>
方法的签名意味着从您可以将这些元素视为 Magicable 的那一刻起,您不关心列表中的内容。打电话时
List<T> list = getMagicables();
你想在不知情的情况下照顾类型T。换句话说,有 3 个用例:T is Magicable (OK), T is Magician (Wrong because getMagicables may return a list of ) and T is (Wrong too ).
- Why I use
? extends Magicable
instead of just Magicable
in lists
因为 List<Magician>
是 List<? extends Magicable>
的子类型,而不是 List<Magicable>
的子类型。这对方法的参数很有用。
public void doIt(List<? extends Magicable> list) {
// you can't add a Magician here
}
可用作
List<Witch> list = ...
doIt(list);
但是如果你有
public void doIt(List<Magicable> list) {
// you can add a Magician here
}
您不能将其用作
List<Witch> list = ...
doIt(list); // compile error
我试图理解 Java 中的泛型类型,理论上它看起来可以理解,但是当我需要将它应用到实际代码时我遇到了问题。我想声明将 return 泛型类型的抽象方法。假设我有一些名为 Magicable 的空接口,并且 2 class 实现了它:Magican 和 。 现在我想知道这 3 个声明之间有什么区别:
/*1*/protected abstract <T extends Magicable> List<T> getMagicables();
/*2*/protected abstract List<? extends Magicable> getMagicables();
/*3*/protected abstract List<Magicable> getMagicables();
在第一种情况下,当我想在扩展抽象 class:
的某些 class 中实现此方法的主体时遇到问题@Override protected List<Magican> getMagicable() {..}
我有警告消息:
Type safety: The return type List<Magican> for getMagicable() from the type MagicanService needs unchecked conversion to conform to List<Magicable> from the type MagicableService.
在第二种情况下我没有这个警告,但是我在抽象 class 中有问题,我在上面声明了抽象方法:
public void <T extends Magicable> T getOneFromList() { List<T> list = getMagicables(); //..... }
在这种情况下,我在 getMagicables() 调用中出现编译错误:
Type mismatch: cannot convert from List<capture#2-of ? extends Magicable> to List<T>
第三种情况导致上述两处代码编译错误。我不认为它是否适合我的情况。
对于问题的部分,您确实向我们展示了方法 /* 3 */ 就足够了,您的那部分代码不需要泛型。但你需要尊重可替代性:
你在 #1 中得到错误,因为子类型方法限制了 return 类型的范围:Magican
是 Magicable
但反之则不然。子类型中允许 Magicable
的超类型。子类型方法必须可以替代超类型方法,而在您的示例中不是这种情况。
#2 中的错误是由于通配符 ?
的性质造成的:? extends Magicable
和 T extends Magicable
不必是同一类型。如果 T
在 class 范围内声明,例如class Magican<T> implements Magicable<T>
(当然,在这种情况下,您的接口需要声明 T)在您的类型中出现的所有 T
都将引用相同的 class.
public abstract class AbstractMagicable<T extends Magicable> {
abstract List<T> getMagicables1();
abstract List<? extends Magicable> getMagicables2();
abstract List<Magicable> getMagicables3();
}
class MagicableWitch extends AbstractMagicable<Witch> {
@Override
List<Witch> getMagicables1() {
return null;
}
@Override
List<? extends Magicable> getMagicables2() {
return getMagicables1();
}
@Override
List<Magicable> getMagicables3() {
return Collections.singletonList(new Witch());
}
}
class MagicableMagician extends AbstractMagicable<Magician> {
@Override
List<Magician> getMagicables1() {
return null;
}
@Override
List<? extends Magicable> getMagicables2() {
return getMagicables1();
}
@Override
List<Magicable> getMagicables3() {
return Collections.singletonList(new Magician());
}
}
1) 当你想在使用它时用真实姓名替换它时使用T。例如 class MagicableWitch extends AbstractMagicable<Witch>
。
此处 Witch
已替换 T,因此 abstract List<T> getMagicables1();
在其具体 class.
List<Witch> getMagicables1()
2)? class 将在运行时可用时使用 class 将被替换。
3) List<Magicable>
和 List<Witch>
是不同的,尽管 Witch implements Magicable
。 getMagicables3
中显示了实现
.
在您的第一种情况下,抽象方法被声明为使用通用类型 <T extends Magicable>
,这意味着您的方法可以 return Magicable 列表或实现它的任何类型。在您的实现中,您正在 return 使用具体类型 Magican,它是一个 Magicable。您可以安全地忽略警告并添加 @SuppressWarning("unchecked")
以禁用警告。需要注意的是,任何扩展您的 class 的 classes 都将被限制为 return 仅魔法师列表。
在第二种情况下,声明 List<T> list = getMagicables();
会抛出错误,因为您的方法不是 return 一个 List<T>
而是一个 List<? extends Magicable'
,这不是一回事.由于泛型的工作方式,当您声明使用未绑定通配符的 return 类型时,任何调用您的方法的代码都必须具有可接受的匹配类型,在您的情况下如 List<? extends Magicable>
或 List<?>
.
关于第三种情况,您的抽象方法 return 是 List<Magicable>
而您的实现 return 是 List<Magic>
。这似乎违反直觉,但您不能在 Java: List<Magicable> list = ArrayList<Magic>
中对泛型执行类似的操作。这可能看起来很奇怪,因为数组允许您声明类似 Magicable[] magics = new Magican[3];
的东西。这是一个常见的误解,因为数组是协变的,而泛型是不变的。协变的意思是,如果你有两个 classes Super
和 Sub extends Super
,Sub[] is a subtype of Super[]
。对于泛型,因为它们是不变的,所以两者之间没有关系,List<Sub>
不是 List<Super>
的子类型。
如果你想 return 泛型类型,只需在 class 扩展你的抽象 class 中使用与第一个案例 protected <T extends Magicable> List<T> getMagicable()
相同的类型声明.在 returned 类型中使用通配符是一个非常糟糕的主意,因为您强制 class 用户在他们的 List 变量声明中使用通配符。
- First case
只需声明您的方法:
@Override
protected <T extends Magicable> List<T> getMagicables() {
List<T> list = ...
return list
}
如果你真的想要这个:
@Override
protected List<Magican> getMagicable() {..}
您可能必须将通用 T 声明到 class 定义中
public abstract class AbstractKlass<T extends Magicable> {
protected abstract List<T> getMagicables();
}
然后在你的 Subclass:
public class MySubClass extends AbstractKlass<Magican> {
@Override
protected List<Magican> getMagicables() {
...
}
}
- Second case
编译错误是正常的,因为 <? extends Magicable>
方法的签名意味着从您可以将这些元素视为 Magicable 的那一刻起,您不关心列表中的内容。打电话时
List<T> list = getMagicables();
你想在不知情的情况下照顾类型T。换句话说,有 3 个用例:T is Magicable (OK), T is Magician (Wrong because getMagicables may return a list of ) and T is (Wrong too ).
- Why I use
? extends Magicable
instead of justMagicable
in lists
因为 List<Magician>
是 List<? extends Magicable>
的子类型,而不是 List<Magicable>
的子类型。这对方法的参数很有用。
public void doIt(List<? extends Magicable> list) {
// you can't add a Magician here
}
可用作
List<Witch> list = ...
doIt(list);
但是如果你有
public void doIt(List<Magicable> list) {
// you can add a Magician here
}
您不能将其用作
List<Witch> list = ...
doIt(list); // compile error