JLS如何指定通配符不能在方法中正式使用? [英] How does the JLS specify that wildcards cannot be formally used within methods?

查看:124
本文介绍了JLS如何指定通配符不能在方法中正式使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直想知道Java泛型的一些奇怪的方面以及通配符的使用。比方说,我有以下的API:

  public interface X< E> {
E get();
E set(E e);
}

然后,我们假设我们声明以下方法:

  public class Foo {
public void foo(X<> x){
//这不能编译:
x.set(x.get());
}

public< T> void bar(X< T> x){
//这个编译:
x.set(x.get());


$ / code>

从我的直观理解中,<$ c $实际上与 X 相同,除了未知的<对于客户端代码,T> 对于形式是未知的。但是在 foo()中,我猜测编译器可以推断(伪JLS代码)< T0> :=<> [0],< T1> :=<> [1]等... 。这是大多数程序员明确而直观地做的。他们委托给一个私人助手方法,这导致了大量无用的锅炉代码:

  public class Foo {
public void foo(X<> x){
foo0(x);
}

私人< T> void foo0(X< T> x){
x.set(x.get());
}
}

另一个例子:

  public class Foo {
public void foo(){
//假设我可以实例化X
X<?> x = new X< Object>();
//在这里,我不能简单地为foo()添加一个泛型类型:
//我没有选择,只能引入一个无用的foo0()辅助方法
x.set(x。得到());


换句话说,编译器知道通配符在 x.set() x.get()中的通配符正式相同。为什么它不能使用这些信息? JLS 中是否有正式的方面,解释这个缺乏编译器的功能?

解决方案

您想知道为什么不支持它?有效的问题是为什么应该支持它?



通配符的工作方式是,给定声明 X <?延伸Y> x ,表达式的类型 x.get()不是?扩展Y 或编译器推断的某种类型,但 Y 。现在, Y 不能分配给?扩展Y ,所以你不能将 x.get()传递给 x.set 其有效签名为 void set(?extends Y e)。唯一可以传递给它的是 null ,它可以分配给每一个引用类型。



我关注JDK开发人员的邮件列表相当密切,总体精神在于JLS规定的每个功能都应该自行承担重量 - 成本/收益比率应该在利益方面很重要。现在,追踪价值的起源是完全可能的,但它只能解决一个特殊情况,它有其他几种解决方法。考虑这些类:

  class ClientOfX {
X<?>会员;
void a(X<> param){
param.set(param.get());
}
void b(){
X<> local = new XImpl< Object>();
local.set(local.get());
}
void c(){
member.set(member.get());
}
void d(){
ClassWeCantChange.x.set(ClassWeCantChange.x.get());
}
}

class ClassWeCantChange {
public static X<?> X;
}

这显然不能编译,但是对于您提出的增强,但是,我们可以在不改变JLS的情况下编译四种方法中的三种:
$ b


  1. 对于 a ,当我们接收泛型对象作为参数时,我们可以使该方法是泛型的并接收 X< T> ,而不是 X< ?>

  2. 对于 b c 微软C#编译器团队的Eric Lippert称这种情况如果这样做会让你感到痛苦,那么不要这么做!)。

  3. 对于 d ,当使用其声明无法控制的引用时,我们别无选择,只能编写一个辅助方法。



    1. 所以智能型系统只有在我们无法控制变量声明的情况下才有用。在这种情况下,做你想做的事情有点粗略 - 泛型通用类型的行为通常意味着对象要么只是作为生产者工作(?extends一些东西)或一个消费者(?super Something )的对象,而不是两个。



      TL; DR版本是它会带来很小的好处(并且要求通配符不是今天的东西!),同时增加JLS的复杂性。如果有人提出一个非常引人注目的案例来添加它,规范可能会延长 - 但是一旦它存在,它就会永远存在,并且可能会导致不可预见的问题,所以请将其保留,直到您真正需要它为止。 p>

      编辑:这些评论包含更多关于通配符是否存在的讨论。


      I've always been wondering about some weird aspect of Java generics and the use of wildcards. Let's say, I have the following API:

      public interface X<E> {
          E get();
          E set(E e);
      }
      

      And then, let's say we declare the following methods:

      public class Foo {
          public void foo(X<?> x) {
              // This does not compile:
              x.set(x.get());
          }
      
          public <T> void bar(X<T> x) {
              // This compiles:
              x.set(x.get());
          }
      }
      

      From my "intuitive" understanding, X<?> is in fact the same as X<T>, except that the unknown <T> is formally unknown to client code. But inside of foo(), I would guess that the compiler could infer (pseudo JLS code) <T0> := <?>[0], <T1> := <?>[1], etc.... This is what most programmers do explicitly and intuitively. They delegate to a private helper method, which leads to a lot of useless boiler-plate code:

      public class Foo {
          public void foo(X<?> x) {
              foo0(x);
          }
      
          private <T> void foo0(X<T> x) {
              x.set(x.get());
          }
      }
      

      Another example:

      public class Foo {
          public void foo() {
              // Assuming I could instanciate X
              X<?> x = new X<Object>();
              // Here, I cannot simply add a generic type to foo():
              // I have no choice but to introduce a useless foo0() helper method
              x.set(x.get());
          }
      }
      

      In other words, the compiler knows that the wildcard in x.set() is formally the same as the wildcard in x.get(). Why can't it use that information? Is there a formal aspect in the JLS, that explains this lacking compiler "feature"?

      解决方案

      You wonder why isn't it supported? An equally valid question is, why should it be supported?

      The way wildcards work, given the declaration X<? extends Y> x, the type of the expression x.get() is not ? extends Y or some type inferred by the compiler, but Y. Now, Y isn't assignable to ? extends Y, so you can't pass x.get() to x.set which has the effective signature of void set(? extends Y e). The only thing you could actually pass to it is null, which is assignable to every reference type.

      I follow the JDK developer mailing lists fairly closely, and the general spirit there is that every feature the JLS specifies should pull its own weight - the cost/benefit ratio should be heavy on the benefit side. Now, keeping track of the origin of a value would be quite possible, but it would only solve one special case, which has several other ways to solve it. Consider these classes:

      class ClientOfX {
          X<?> member;
          void a(X<?> param) {
              param.set(param.get());
          }
          void b() {
              X<?> local = new XImpl<Object>();
              local.set(local.get());
          }
          void c() {
              member.set(member.get());
          }
          void d() {
              ClassWeCantChange.x.set(ClassWeCantChange.x.get());
          }
      }
      
      class ClassWeCantChange {
          public static X<?> x;
      }
      

      This obviously doesn't compile, but with your proposed enhancement, it does. However, we can get three of the four methods compiling without changing the JLS:

      1. For a, when we receive the generic object as a parameter, we can make the method generic and receive an X<T> instead of X<?>.
      2. For b and c, when using a locally declared reference, we don't need to declare the reference with a wildcard (Eric Lippert of Microsoft's C# compiler team calls this situation "if it hurts when you do that, then don't do that!").
      3. For d, when using a reference whose declaration we can't control, we have no option but to write a helper method.

      So the smarter type system would really only help in the case where we can't control the declaration of a variable. And in that situation, the case for doing what you want to do is kind of sketchy - the very act of wildcarding a generic type normally means that the object is either intended to work only as a producer (? extends Something) or a consumer (? super Something) of objects, not both.

      The TL;DR version is that it would bring little benefit (and require wildcards to be something else than what they are today!) while adding complexity to the JLS. If someone comes up with a very compelling case to add it, the spec could be extended - but once it's there, it's there forever, and it may cause unforeseen problems down the road, so leave it out until you really need it.

      Edit: The comments contain more discussion about what wildcards are and aren't.

      这篇关于JLS如何指定通配符不能在方法中正式使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆