多个通配符泛型方法的正确语法?

时间:2011-03-09 17:14:25

标签: java generics compiler-errors wildcard

我在使用多个通配符

的通用方法获取语法时遇到了问题

首先我需要两个不同的通配符,因为(虽然我已经看到'?'可以用来表示两种不同的类型)编译器在使用'?'时会怎么知道方法中含糊不清。

所以下面我有非常非法的版本,我使用'*'作为第二个通配符(我可以使用其他一些通配符吗?)

似乎方法中存在编程错误但我可以从类型'*'构造类型'T'(我有这样的构造函数 - 基本上将Google协议缓冲区放入并制作一个“完全成熟的对象” “出于它”

private <T, ? extends Database<*>, * extends GeneratedMessage> T getItem(String key, ? db, Hashtable<String, T> table, String message) throws GadsDataException {
    T returnValue = table.get(key);
    if (returnValue == null) {
        * temp = null;
        try {
            temp = db.get(key);
        }
        catch  (InvalidProtocolBufferException e) {
            throw new GadsDataException( message + key + " and hit: ", e);
        }
        if (temp != null) {
            returnValue = new T(temp);
            table.put(key, returnValue);
        }
    }
    return returnValue;
}

在绝望中我尝试了一个没有任何通配符的版本(我不喜欢它,因为它不像第一个那样保留类型关系)。但编译器也不太关心它。我想我应该看看另一张海报上提到的“变形金刚”的事情。

private <T, D, G> T getItem(String key, D db, Hashtable<String, T> table, String message) throws GadsDataException {
        T returnValue = table.get(key);
        if (returnValue == null) {
            G temp = null;
            try {
                temp = db.get(key);
            }
            catch  (InvalidProtocolBufferException e) {
                throw new GadsDataException( message + key + " and hit: ", e);
            }
            if (temp != null) {
                returnValue = new T(temp);
                table.put(key, returnValue);
            }
        }
        return returnValue;
    }

所以我仍然坚持构造函数(正如其他海报所表明的那样)。我尝试通过制作一个'人工'基类

来让编译器感到高兴
/**
 * This is created as a base class for all objects that can
 * construct themselves from a Google Protocol Buffer with one
 * parameter.
 * 
 * This is just done for the sake of templatizing the methods
 * inside of the GadsLite API
 *
 */
public class ConstructorOneParameter {

    ConstructorOneParameter(GeneratedMessage G) {
        // don't actually do anything... the derived
        // class does all the work
        // We're doing this just to templatize the GadsLite methods
    }
}

然后我用它来识别模板方法中的类型。编译器仍然不高兴。

private <T extends ConstructorOneParameter, D extends Database<G>, G extends GeneratedMessage> T 
        getItem(String key, D db, Hashtable<String, T> table, String message) throws GadsDataException {
    T returnValue = table.get(key);
    if (returnValue == null) {
        G temp = null;
        try {
            temp = db.get(key);
        }
        catch  (InvalidProtocolBufferException e) {
            throw new GadsDataException( message + key + " and hit: ", e);
        }
        if (temp != null) {
            returnValue = new T(temp);
            table.put(key, returnValue);
        }
    }
    return returnValue;
}

有几个人建议工厂,所以我更改了行“returnValue = new T(temp);” into“returnValue = MessageObjectFactory create(temp);”然后对于工厂我有:

public class MessageObjectFactory {

    public static <G extends GeneratedMessage, R extends Object> R create(G message) {
        R returnValue = null;
        if (message instanceof ArtccData.Artcc) {
            returnValue = new Artcc((ArtccData.Artcc)message);
        }
        return returnValue;
    }

}

我现在正在做一种类型作为概念证明。不幸的是,即使所有内容都是Object的子类,编译器也不满意“returnValue = new Artcc(...”行抱怨“无法从Artcc转换为R”。是否有“变压器工厂”的具体示例?

3 个答案:

答案 0 :(得分:1)

首先关闭 - 在通用参数部分中声明通配符(?)时,绝对零点。该部分用于声明您将在实际方法签名中引用的类型变量&amp;身体。如果你在那里放一个通配符,你显然不能引用它们 - 如果你没有引用它们,它们就不属于通用参数(因为你的方法不会根据它们的值而变化)。 / p>

你问

  

当我使用'?'时,编译器将如何知道?方法中含糊不清。

我觉得很困惑。很明显它不会,正是为什么你在通用参数块中给出了类型名称!

因此,在第一个示例中,您尝试使用通配符完成了什么?如果您将?替换为U*替换为V(显然实际字母是任意名称),为什么不会起作用,如:

private <T, V extends GeneratedMessage, U extends Database<V>> T getItem(String key, U db, Hashtable<String, T> table, String message) throws GadsDataException {
    ...
    V temp = null;
    ...
}

(您可能会注意到我切换了后两个参数的顺序 - 不允许前向引用,因此V必须出现在依赖于它的U之前。

这类似于第二个示例,除了它维护数据库和消息参数类型的约束。编译器对你的版本不喜欢什么?

答案 1 :(得分:1)

首先,你必须找到比

更好的方法
new T(...)

因为这是不允许的

答案 2 :(得分:1)

  

由于我的所有类型T都有T(G)形式的构造函数,我不明白为什么我不能new T(temp)。我还编辑了原始问题,以显示上一条评论中描述的失败黑客。

Java泛型是一种保证类型安全的工具,而不是根据参数创建不同的代码。编译器擦除大多数类型变量,因此在运行时实际类型T根本不知道 - 因此我们不能调用它的任何构造函数(或创建此类型的数组,方式)。通过这种方式,我们可以将泛型方法(或类)与创建方法时尚不存在的类型一起使用。

实际上,编译器无法保证所有可能的类都有这样的构造函数,因为以后的子类总是没有。

因此,在您的情况下,您应该考虑使用某种工厂对象,它需要G并返回正确的T对象,(例如Transformer<G, T>类型,就像Andrzej提出的那样。)