简体   繁体   English

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

[英]Type variables with limited scope in Java

I'm wondering if it's possible to introduce type variables within the scope of a method in Java. 我想知道是否有可能在Java的方法范围内引入类型变量。 That is, limiting their scope within the method body. 也就是说,限制它们在方法体内的范围。

However, rather than trying to describe the problem in the abstract, let my illustrate with my concrete problem. 然而,不是试图在抽象中描述问题,而是让我用我的具体问题来说明。 I have a couple of classes that look a bit like this: 我有几个类看起来像这样:

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

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

I now want to write a function, in Settings , for setting the value of all the Settings to their fallback values as provided by the abstract method. 我现在想在Settings编写一个函数,用于将所有设置的值设置为抽象方法提供的回退值。 My first thought would be to do it like this: 我的第一个想法是这样做:

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

However, on second thought it is rather obvious why this does not work; 然而,在第二个想法中,为什么这不起作用是相当明显的; the capture of <?> for setting.value is not the same capture as for setting.fallbackvalue() . set.value的<?>捕获与setting.fallbackvalue()捕获不同。 Ergo, I need some way to unify the captures. 因此,我需要一些方法来统一捕获。 It is possible to solve it like this: 可能像这样解决它:

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

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

The explicit type variable <T> for reset1 unifies the captures conveniently, but it's obviously the ugliest thing in the world to introduce this function, pollute the namespace, clutter the screen and make the code less readable merely to satisfy the type system. reset1的显式类型变量<T>可以方便地统一捕获,但显然世界上最丑的东西是引入这个函数,污染命名空间,使屏幕变得杂乱,使代码只是为了满足类型系统而不易读取。

Is there no way I can do this within the body of reset ? 我无法在reset体内做到这一点吗? What I'd like to do is simply something like this: 我想做的只是这样的事情:

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

It's not the prettiest thing in the world, but at least to my eyes it is far less strainful than the variant above. 它不是世界上最漂亮的东西,但至少在我看来,它比上面的变体要小得多。 Alas, it's not possible; 唉,这是不可能的; but what is possible, then? 但那有什么可能呢?

There's no way to do what you're asking without changing other aspects of your code. 在不改变代码的其他方面的情况下,没有办法做你要求的。 However (to solve your particular issue), you can write a reset method in the inner Setting class: 但是(为了解决您的特定问题),您可以在内部Setting类中编写一个reset方法:

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

Then your loop (in the reset method of the Settings class) would simply be: 那么你的循环(在Settings类的reset方法中)就是:

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

No... 没有...

Although wildcard capture does introduce new type variables, they are only available to the compiler; 虽然通配符捕获确实引入了新类型变量,但它们仅适用于编译器; there's no way for programmer to access them directly. 程序员无法直接访问它们。

Currently, only class/method can introduce type variables. 目前,只有类/方法可以引入类型变量。 Therefore the only way to convert an expression type with wildcards to a type without wildcard is to pass the expression through a method (or a constructor with diamond inference new Foo<>(setting) which is essentially the same mechanism) 因此,将带有通配符的表达式类型转换为不带通配符的类型的唯一方法是通过方法(或带有钻石推理的构造函数new Foo<>(setting)传递表达式,这基本上是相同的机制)

Your reset1 thing is the generally accepted way to do it. 你的reset1是普遍接受的做法。 It's called a "capture helper", and is an often-cited pattern in generics. 它被称为“捕获助手”,并且是泛型中经常被引用的模式。 It usually comes up in a situation like the following: 它通常出现在以下情况中:

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

In this case, you need to both get something out of the parameterized type, and put something back in of that type. 在这种情况下,您需要从参数化类型中获取某些内容,并将某些内容放回该类型中。 A wildcard just won't let you do that. 通配符只是不允许你这样做。 This is just like your case, because you are also getting something out and putting something in. We know that the type must be the same, but wildcards are too weak to enforce that. 这就像你的情况一样,因为你也得到一些东西并放入一些内容。我们知道类型必须相同,但通配符太弱而无法强制执行。 Only an explicit type variable can allow us to do it: 只有显式类型变量才允许我们这样做:

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

However, we don't want this extraneous <T> that is only used in only one place in the argument list. 但是,我们不希望这个无关的<T>仅在参数列表中的一个位置使用。 To the outside world, swap(List<?> list, int i, int j) should work perfectly fine. 对外界来说, swap(List<?> list, int i, int j)应该可以正常工作。 That we need to use this <T> type parameter is an implementation detail that nobody needs to know. 我们需要使用这个<T>类型参数是一个没有人需要知道的实现细节。 So to hide it, we wrap the generic function with the function that takes the wildcard: 因此,要隐藏它,我们使用带通配符的函数包装泛型函数:

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

It does seem a waste, but that's how it is. 这似乎是浪费,但事实就是如此。

Given the analogous situation between your situation and this one, and the fact that the capture helper is the canonical solution in this situation, I can tell you with confidence that, no, there is no better way to do it. 鉴于你的情况和这个情况之间的类似情况,以及捕获助手是这种情况下的规范解决方案这一事实,我可以自信地告诉你,不,没有更好的方法来做到这一点。

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

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