Java 这个通用方法签名有意义吗?
遗留代码:Java 这个通用方法签名有意义吗?,java,generics,Java,Generics,遗留代码: public <B extends IBox> List<B> getBoxList(String key) public List getBoxList(字符串键) 方法无法知道调用方实际期望的子类型,因此没有合理的方法来实现此约定 重要提示:无法从键推断预期的子类型 因此,签名应为: public List<IBox> getBoxList(String key) public List getBoxList(字符串键) 我的推理正确吗
public <B extends IBox> List<B> getBoxList(String key)
public List getBoxList(字符串键)
方法无法知道调用方实际期望的子类型,因此没有合理的方法来实现此约定
重要提示:无法从键推断预期的子类型
因此,签名应为:
public List<IBox> getBoxList(String key)
public List getBoxList(字符串键)
我的推理正确吗?你可以很容易地这样做:
public List<? extends IBox> getBoxList(String key)
public List两者之间有细微的区别。第一个保留类型,而第二个将类型展平到其接口。通过移动到第二个,您实际上是在丢弃一些可能对调用方有用的信息
interface IBox {
}
public static <B extends IBox> List<B> getBoxList1(String key) {
return null;
}
public static List<IBox> getBoxList2(String key) {
return null;
}
class ABox implements IBox {
}
class BBox implements IBox {
}
public void test() {
List<ABox> aBox = Test.<ABox>getBoxList1("Hello");
List<BBox> bBox = Test.<BBox>getBoxList1("Hello");
// Not allowed.
List<ABox> cBox = Test.getBoxList2("Hello");
List<IBox> dBox = Test.getBoxList2("Hello");
}
接口IBox{
}
公共静态列表getBoxList1(字符串键){
返回null;
}
公共静态列表getBoxList2(字符串键){
返回null;
}
类ABox实现了IBox{
}
类BBox实现IBox{
}
公开无效测试(){
List aBox=Test.getBoxList1(“你好”);
List bBox=Test.getBoxList1(“Hello”);
//不允许。
List cBox=Test.getBoxList2(“Hello”);
List dBox=Test.getBoxList2(“Hello”);
}
为了解释这一点,让我们从一个例子开始
public RedBox implements IBox{
//implementation here
}
public BlueBox implements IBox{
//implementation here
}
现在假设有一个例子,我需要RedBoxe和YellowBox的列表,那么根据第一个代码,我的调用将是
List<RedBox> redBoxList = getBoxList(redKey);
List<YellowBox> yellowBoxList = getBoxList(yellowKey);
List redBoxList=getBoxList(redKey);
List yellowBoxList=getBoxList(yellowKey);
但在后一种情况下,这将是:
List<RedBox> redBoxList = (List<RedBox>)getBoxList();//Since it returns List<IBox>
List<RedBox> redBoxList = (List<RedBox>)getBoxList();
List redBoxList=(List)getBoxList()//因为它返回列表
List redBoxList=(List)getBoxList();
因此,前一个代码在确保类型安全方面非常有效。
以前的代码更有意义。要了解有关泛型的更多信息,请参考有效的Java。不,它没有。考虑下面的代码,这些代码甚至不会编译在java 8下,但会在java 7下编译并发出警告。调用者实际上希望从BoxDB获得一个BlueBox列表:
List<BlueBox> boxList = boxDB.<BlueBox>getBoxList("123");
List-boxList=boxDB.getBoxList(“123”);
但他实际上得到的是一张红盒子的清单。因此,这种方法没有兑现其承诺
import java.util.*;
public class HelloWorld
{
public static void main(String[] args)
{
BoxDB boxDB = new BoxDB();
List<BlueBox> boxList = boxDB.<BlueBox>getBoxList("123");
for (IBox box: boxList) {
System.out.println(box.getClass().getName());//prints "RedBox"
}
}
}
interface IBox {
String getKey();
}
class RedBox implements IBox {
String key;
public RedBox(String key) {
this.key = key;
}
public String getKey() {
return key;
}
}
class BlueBox implements IBox {
String key;
public BlueBox(String key) {
this.key = key;
}
public String getKey() {
return key;
}
}
class BoxDB
{
public <B extends IBox> List<B> getBoxList(String key) {
List<B> result = new ArrayList<>();
result.add((B)new RedBox("123"));
return result;
}
}
import java.util.*;
公共类HelloWorld
{
公共静态void main(字符串[]args)
{
BoxDB BoxDB=新的BoxDB();
List-boxList=boxDB.getBoxList(“123”);
用于(IBox:boxList){
System.out.println(box.getClass().getName());//打印“RedBox”
}
}
}
接口IBox{
字符串getKey();
}
类RedBox实现了IBox{
字符串键;
公共红色框(字符串键){
this.key=key;
}
公共字符串getKey(){
返回键;
}
}
类BlueBox实现了IBox{
字符串键;
公共蓝盒(字符串键){
this.key=key;
}
公共字符串getKey(){
返回键;
}
}
类BoxDB
{
公共列表getBoxList(字符串键){
列表结果=新建ArrayList();
结果。添加((B)新的红色框(“123”);
返回结果;
}
}
我不明白这个问题。为什么您认为第二个选项更好?可以将泛型方法调用为foo。getBoxList(…)
。我声明不可能履行合同,当然您可以始终强制转换结果。@Roland:有可能履行合同--只需返回null
--它只是不太有用。对了,您将如何实现getBoxList1
的方法体来实例化预期的返回类型?@Roland-也许键会将我指向工厂或构造函数?这里的本质是,第一个保留了最大数量的类型信息,而第二个丢弃了其中的一些信息。我将尝试发布一个更真实的演示。@OldCurmudgeon:B
和键
完全由调用方独立决定。好的,您将如何实现getBoxList的方法体来实例化预期的返回类型?前者肯定是非常不安全的类型<代码>列表yellowBoxList=getBoxList(红键)
完全合法。@Roland这要看情况而定。如果方法的实现类似于工厂或服务,那么它可以根据传递给方法getBoxList(红色)的键来决定返回类型。同样,返回类型表明,无论用户返回什么,都应该实现IBox接口。如果类型参数不是必需的,为什么需要单独的类型参数呢。类型参数在大多数情况下是有意义的,但不是所有情况。这与这两种情况无关。