【问题标题】:Generic Method Pattern In JavaJava中的泛型方法模式
【发布时间】:2012-02-02 00:43:25
【问题描述】:

我对 Java 泛型非常陌生,并且花费了大量时间寻找合适的解决方案(如果有的话)。

我正在尝试设计处理特定类型类对象的适配器。如下所述,CAdapter 类只处理“CClass”对象。我正在尝试提供一个抽象的通用适配器来处理大部分工作(很像 LinkedList 等 Java 集合)。然后,我为需要支持的每种类型提供了一个具体的适配器实现。

// Classes

public interface AInterface {
  public String toString();
}

public class BClass extends AInterface  {
  public String toString() { return "BClass "; }
}

public class CClass extends AInterface  {
  public String toString() { return "CClass"; }
}

// Adapters

public interface AdapterInterface<T extends AInterface> {
  public T getInterface();
}

public class BAdapter implements AdapterInterface<BClass> {
  private BClass aInterface = null;
  public BClass getInterface() { return aInterface; }
}

public class CAdapter implements AdapterInterface<CClass> {
  private CClass aInterface = null;
  public CClass getInterface() { return aInterface; }
}

首先,我读到为这样的通用适配器提供一个具体的实现是不受欢迎的(关于上帝杀死一只小猫的事情)!也许有人可以对此进行扩展?

其次,我遇到了动态实例化适配器并且没有 Java 编译器抱怨的问题。比如我有方法:

public <T extends AInterface> AdapterInterface<T> getAdapter(String type) {
  AdapterInterface<T> result = null;
  if (type.equals("C") {
    result = new CAdapter();
  }
  return result;
}

当然,编译器会抱怨 CAdapter not matching 。给定任何类型的 AInterface 对象,我希望能够加载正确的适配器并适当地处理它。为了实现这一点,我无法理解工厂模式。

任何想法将不胜感激。

【问题讨论】:

  • 你在调用 foo. getAdapter(C) 吗?否则 T 是未绑定的。更常见的模式是 public AdapterInterface getAdapter(Class clazz);
  • 您必须为杀死小猫的部分提供一些背景信息。如果我们不知道互联网上的某个人实际上说了什么,我们就无法告诉您他是对还是错。
  • @Nialscorva 这并不重要。无论您是否提供类型标记,类型系统都无法证明约束。当方法签名类似于&lt;T&gt; T getWhateverOfType(Class&lt;T&gt; type) 时,类型标记很有用,因为Class.newInstance()Class.cast() 等。如果您不处理给定类的实例,它们就没有用。
  • @Nialscorva 即如果您可以将AdapterInterface&lt;CClass&gt;.class 传递给方法参数,这将有所帮助,但如果没有超类型标记,这是不可能的。 (而且我认为它们实际上不会帮助解决这里的问题,而不是静态地使代码类型安全。)
  • 当我看到AdapterInterface 中的代码时,我要做的第一件事就是在浏览器中打开Langer's Generics FAQ

标签: java generics design-patterns


【解决方案1】:

我的回答有点多余,但是:

任何带有 的东西都意味着“我的调用者知道这种类型是什么,但我不知道”。所以

AdapterInterface<T> result = null;

意思是“我实际上不知道结果是什么类型,它是我的调用者认为的任何类型”。编译器对此抱怨:

result = new CAdapter();

因为这段代码不能假设 T 是 CClass。

事实上,no 没有强制转换的方法(将方法声明为通配符只是意味着您需要在调用它的地方强制转换结果)。演员表是你告诉编译器“我知道你无法知道这是什么,没关系:我知道。相信我。冷静。”的方式。是的,您会收到警告。没关系。

泛型不会消除所有强制转换,但它们允许您只执行一次。您无需在所有地方都需要演员,而是在您(编码员)确定您正在玩类型游戏的那个地方需要它们。其余所有代码,使用您刚刚创建的适配器的 suff,都可以安全地使用泛型类型。

【讨论】:

  • 感谢您的回答。我很感激它解释得非常清楚。
  • 我在窃取“我的调用者知道这种类型是什么,但我不知道”这样的措辞,这比我倾向于解释的方式更清楚。
【解决方案2】:

不要在这里使用泛型,协变(或逆变,永远不记得哪个是哪个)返回类型似乎可以满足您的需求:

interface AdapterInterface {
    public AInterface getInterface();
}

class BAdapter implements AdapterInterface {
    private BClass aInterface = null;

    public BClass getInterface() {
        return aInterface;
    }
}

class CAdapter implements AdapterInterface {
    private CClass aInterface = null;

    public CClass getInterface() {
        return aInterface;
    }
}

public AdapterInterface getAdapter(String type) {
    AdapterInterface result = null;
    if (type.equals("C")) {
        result = new CAdapter();
    }
    return result;
}

除非你没有提到的接口中还有其他一些方法。以下内容也可以使用泛型类进行编译:

public
AdapterInterface<? extends AInterface> getAdapter(String type) {
    if (type.equals("C")) {
        return new CAdapter();
    } else {
        // …
    }
}

您的原始方法无法编译的原因是T 是扩展AInterface 的特定未知类型;这并不意味着“从AInterface 扩展的任何类型”。无法静态证明您返回的适配器是调用者想要的类型的适配器。

【讨论】:

  • 感谢您的意见。在您的答案和下面的答案(PaulMurrayCbr)之间,我想我终于掌握了如何处理 Java 泛型 - 特别是在使用工厂设计模式时。我将简单地在工厂执行一次强制转换并抑制警告,理解泛型(至少在 Java 5/6 中)不能消除所有强制转换。
  • @MichaelCronk 没错。很多关于 Java 泛型的 SO 问题实际上往往是 OP 询问如何实现他正在考虑的类型约束,只是为了找出为什么它不可能。但是,您可以并且应该做的是尽量避免未经检查的转换(转换为类型参数和参数化类型),方法是将它们替换为Class.cast() 和其他运行时类型检查。或者尽可能将它们封装在你知道是正确的代码中,即使类型系统无法验证它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-02
  • 1970-01-01
相关资源
最近更新 更多