3

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

但是,与其试图抽象地描述问题,不如让我用我的具体问题来说明。我有几个看起来有点像这样的类:

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

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

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

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

然而,再想一想,为什么这不起作用是相当明显的。for setting.value的捕获与<?>for 的捕获不同setting.fallbackvalue()。因此,我需要一些方法来统一捕获。可以这样解决:

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

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

显式类型变量<T>forreset1方便地统一捕获,但仅仅为了满足类型系统,引入这个函数,污染命名空间,混乱屏幕,降低代码可读性,显然是世界上最丑陋的事情。

有没有办法我可以在体内做到这一点reset?我想做的就是这样:

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

这不是世界上最漂亮的东西,但至少在我看来,它远没有上面的变体那么紧张。唉,这是不可能的;但是有什么可能呢?

4

3 回答 3

3

如果不更改代码的其他方面,就无法按照您的要求进行操作。但是(为了解决您的特定问题),您可以reset在内部Setting类中编写一个方法:

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

那么你的循环(在类的reset方法中Settings)将只是:

for (Setting<?> setting : settings)
    setting.reset();
于 2012-11-15T03:47:56.123 回答
2

不...

尽管通配符捕获确实引入了新的类型变量,但它们仅对编译器可用;程序员无法直接访问它们。

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

于 2012-11-15T04:13:04.817 回答
1

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

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);
}

这似乎是一种浪费,但事实就是如此。

鉴于您的情况与此情况相似,并且捕获助手是这种情况下的规范解决方案这一事实,我可以自信地告诉您,不,没有更好的方法可以做到这一点。

于 2012-11-15T09:43:38.003 回答