在Java中键入范围有限的变量

时间:2012-11-15 03:27:09

标签: java generics

我想知道是否可以在Java的方法范围内引入类型变量。也就是说,限制它们在方法体内的范围。

然而,不要试图在摘要中描述问题,而是让我用我的具体问题来说明。我有几个类看起来像这样:

public class Settings {
    public static abstract class Setting<T> {
        public T value;
        public abstract T fallbackvalue();
    }

    private final List<Setting<?>> settings;
}

我现在想在Settings中编写一个函数,用于将所有设置的值设置为抽象方法提供的回退值。我的第一个想法是这样做:

public void reset() {
    for(Setting<?> setting : settings)
        setting.value = setting.fallbackvalue();
}

然而,在第二个想法中,为什么这不起作用是相当明显的;对于setting.value,<?>的捕获与setting.fallbackvalue()的捕获不同。因此,我需要一些方法来统一捕获。 可能可以像这样解决它:

private static <T> void reset1(Setting<T> s) {
    s.value = setting.fallbackvalue();
}

public void reset() {
    for(Setting<?> setting : settings)
        reset1(setting);
}

<T>的显式类型变量reset1可以方便地统一捕获,但显然世界上最丑的是引入此函数,污染命名空间,使屏幕混乱并使代码不易读取只是为了满足类型系统。

我无法在reset的身体内做到这一点吗?我想做的就是这样的事情:

public void reset() {
    for(Setting<?> setting : settings) {
        <T> {
            Setting<T> foo = setting;
            foo.value = foo.fallbackvalue();
        }
    }
}

这不是世界上最漂亮的东西,但至少在我看来,它比上面的变种要小得多。唉,这是不可能的;但那有什么可能呢?

3 个答案:

答案 0 :(得分:3)

在不改变代码的其他方面的情况下,没有办法做你所要求的。但是(为了解决您的特定问题),您可以在内部reset类中编写Setting方法:

public void reset() {
    value = fallbackvalue();
}

然后你的循环(在reset类的Settings方法中)就是:

for (Setting<?> setting : settings)
    setting.reset();

答案 1 :(得分:2)

没有...

虽然通配符捕获确实引入了新类型变量,但它们只能用于编译器;程序员无法直接访问它们。

目前,只有类/方法可以引入类型变量。因此,将带有通配符的表达式类型转换为不带通配符的类型的唯一方法是通过方法(或带有钻石推理的构造函数new Foo<>(setting)传递表达式,这基本上是相同的机制)

答案 2 :(得分:1)

你的reset1事是普遍接受的方式。它被称为&#34;捕获助手&#34;,并且是泛型中经常被引用的模式。它通常出现在以下情况中:

public void swap(List<?> list, int i, int j) { // swap elements i and j of the list
    // how to write?
}

在这种情况下,您需要从参数化类型中获取某些内容,并将某些内容放回该类型中。通配符只是让你不能这样做。这就像你的情况一样,因为你也得到一些东西并放入一些内容。我们知道类型必须相同,但通配符太弱而无法强制执行。只有显式类型变量才允许我们这样做:

public <T> void swap(List<T> list, int i, int j) {
    T tmp = list.get(i);
    list.set(i, list.get(j));
    list.set(j, tmp);
}

但是,我们不希望这个无关的<T>仅在参数列表中的一个位置使用。对于外面的世界,swap(List<?> list, int i, int j)应该完美无缺。我们需要使用这个<T>类型参数是一个没有人需要知道的实现细节。因此,要隐藏它,我们使用带通配符的函数包装泛型函数:

private <T> void swap_private(List<T> list, int i, int j) { ... }
public void swap(List<?> list, int i, int j) {
    swap_private(list, i, j);
}

看起来确实很浪费,但事情就是如此。

鉴于你的情况和这个情况之间的类似情况,以及捕获助手是这种情况下的规范解决方案的事实,我可以自信地告诉你,不,没有更好的方法来做到这一点。