Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/384.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java为什么不应该';不允许在变量声明上使用泛型类型声明?_Java_Generics_Language Design - Fatal编程技术网

Java为什么不应该';不允许在变量声明上使用泛型类型声明?

Java为什么不应该';不允许在变量声明上使用泛型类型声明?,java,generics,language-design,Java,Generics,Language Design,假设我们有这样一个类: public class xx { public interface Foo<T> { T getValue(); void setValue(T value); } public void resetFoos(Iterable<Foo<?>> foos) { for (Foo<?> foo : foos) foo.setVa

假设我们有这样一个类:

public class xx {

    public interface Foo<T> {
        T getValue();
        void setValue(T value);
    }

    public void resetFoos(Iterable<Foo<?>> foos) {
        for (Foo<?> foo : foos)
            foo.setValue(foo.getValue());
    }
}
这一直让我恼火。另外,似乎有一个简单的解决方案

我的问题:有什么“好”的理由不应该扩展java语言以允许对变量声明进行泛型类型声明吗?例如:

public class xx {

    public interface Foo<T> {
        T getValue();
        void setValue(T value);
    }

    public void resetFoos(Iterable<Foo<?>> foos) {
        for (Foo<?> foo : foos) {
            final <T> Foo<T> typedFoo = foo;
            foo.setValue(foo.getValue());
        }
    }
}
我想知道是否有编译器向导可以解释为什么这样做太难,或者可以(而且应该)这样做

编辑:

针对这一建议的解决方案:

public <T> void resetFoos(Iterable<Foo<T>> foos) {
    for (Foo<T> foo : foos) {
        foo.setValue(foo.getValue());
    }
}
编译错误为:

方法
resetFoos
不能应用于给定类型

必需:
Iterable

找到:
List>
符合形式参数类型
Iterable
其中,
T
是一个类型变量:
T扩展在方法
resetFoos(Iterable)


(通过ideone.com使用sun-jdk-1.7.0_10)

您可以使
resetFoos
方法本身具有通用性:

public <T> void resetFoos(Iterable<Foo<T>> foos) {
    for ( Foo<T> foo : foos)
        foo.setValue(foo.getValue());
}
public void resetFoos(Iterable foos){
for(Foo-Foo:foos)
setValue(foo.getValue());
}
这样编译器就知道
foo.getValue()
中的
T
foo.setValue()
中的
T
是相同的


这只是解决办法。我不知道为什么编译器不允许您在变量级别声明泛型,例如
final Foo typedFoo=Foo;我只知道不能在变量级别声明它。但是,在这里,方法级别已经足够了。

这可能是可以做到的,可能有一些边缘情况。但事实上,没有做到这一点的原因很简单,JLS人员没有做到这一点

类型系统无法推断程序的所有内容。它可以做很多事情,但总有一些地方,编译器/类型检查器不知道人类可以证明的东西。因此,设计编译器和类型检查器的人必须决定在推理和智能方面要走多远——不管他们停在哪里,总会有人提出这样一个问题:“他们为什么不多走一步?”


我的猜测——这只是一个猜测——是这个推论没有成功,因为(a)它很容易解决(如Rgetman所示),(b)它使语言复杂化,(b.1)它引入了棘手的边缘情况,其中一些甚至可能与语言的其他功能不兼容,(c)JLS的设计师觉得他们没有时间进行这项工作。

问题在于,在相关声明中:

foo.setValue(foo.getValue());
两次出现的
可以(据编译器所知)绑定到不同的类型。编译器不知道一个
(由
getValue()
返回)与
setValue
中预期的
不一样,这似乎很愚蠢。但在其他情况下,结果并不那么明确。如果类型参数应该是相同的,则该语言要求它们的名称相同


中提供了一个很好的解决方法。

对java语言的每次更改都将非常困难

您的示例并不是真正需要类型变量。编译器已经通过通配符捕获创建了一个类型变量,只是类型变量对程序员不可用。可能有几种补救办法

  • 让程序员访问类型变量。这不是一项容易的任务。我们需要发明一些奇怪的语法和语义。这种奇怪和复杂可能无法证明这种好处的合理性

  • 共享捕获转换。现在,每个表达式都分别经过捕获转换。规范没有识别出
    foo
    foo
    ,因此这两个表达式应该共享相同的捕获转换。它在一些简单的情况下是可行的,例如,一个有效的最终局部变量应该只转换一次,而不是每次访问

  • 推断变量类型-
    var foo2=foo
    从右侧推断出
    foo2
    的类型,它实际上首先经历捕获转换,因此
    foo2
    的类型将是
    Foo(对于某些x
    ),带有一个新的类型变量x(仍然不明显)。或者直接在
    foo
    -
    上为(var-foo:foos)
    使用
    var
    。推断变量类型是一种成熟的/经过测试的技术,其好处远比解决这个问题更广泛。所以我会投这个票。如果我们能活那么久,它可能会出现在Java9中


  • 基本上,类型变量在Java中目前只能有两个作用域:1)类作用域,或2)方法作用域。您会问,为什么不允许另一个作用域——本地代码块的作用域(在本例中,是for循环的内部)

    是的,在某些情况下,这会很有帮助。将其添加到语言中并不难。但是,这些情况非常罕见,并且比帮助更容易让人困惑。此外,正如您已经发现的,存在一种相对简单而有效的解决方法--将本地块范围移动到一个私有帮助程序函数,它不能使用方法作用域类型变量时:

    public void resetFoos(Iterable<Foo<?>> foos) {
        for (Foo<?> foo : foos) {
            resetFoo(foo);
        }
    }
    
    private <T> void resetFoo(Foo<T> foo) {
        foo.setValue(foo.getValue());
    }
    
    public void resetFoos(Iterable foo:foos){
    重置foo(foo);
    }
    }
    私有无效重置Foo(Foo-Foo){
    setValue(foo.getValue());
    }
    

    是的,通过进行额外的方法调用,这可能会降低代码的效率,但这是一个次要问题。

    为什么不使用
    public void resetFoos(Iterable foos){…}
    ?伙计们,
    Iterable!=Iterable@Archie你的观点是什么?我们想知道你为什么不想使用这个解决方案。因为它不能解决
    IterableI的问题。我想把我的问题用黑体字写出来,我是想弄清楚实际的问题是什么,但显然不是!:)没有真正回答这个问题。但是每个Foo都必须有相同的类型参数
    
    public static class FooImpl<T> implements Foo<T> {
        
        private T value;
        
        public FooImpl(T value) { this.value = value; }
        
        @Override public T getValue() { return value; }
        
        @Override public void setValue(T value) { this.value = value; }
    }
    
    public static <T> void resetFoos(Iterable<Foo<T>> foos) {
        for (Foo<T> foo : foos) {
            foo.setValue(foo.getValue());
        }
    }
    
    public static void main(String[] args) {
        
        final Foo<Object> objFoo = new FooImpl<>(new Object());
        final Foo<Integer> numFoo = new FooImpl<>(new Integer(42));
        final Foo<String> strFoo = new FooImpl<>("asdf");
        
        List<Foo<?>> foos = new ArrayList<>(3);
        foos.add(objFoo);
        foos.add(numFoo);
        foos.add(strFoo);
        
        resetFoos(foos); // compile error
        
        System.out.println("done");
    }
    
    public <T> void resetFoos(Iterable<Foo<T>> foos) {
        for ( Foo<T> foo : foos)
            foo.setValue(foo.getValue());
    }
    
    foo.setValue(foo.getValue());
    
    public void resetFoos(Iterable<Foo<?>> foos) {
        for (Foo<?> foo : foos) {
            resetFoo(foo);
        }
    }
    
    private <T> void resetFoo(Foo<T> foo) {
        foo.setValue(foo.getValue());
    }