繁体   English   中英

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

[英]Type variables with limited scope in Java

我想知道是否有可能在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();
}

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

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

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

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

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

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

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

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

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

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

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

没有...

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM