首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >带有泛型抛出子句的Lambdas和函数接口

带有泛型抛出子句的Lambdas和函数接口
EN

Stack Overflow用户
提问于 2014-06-13 14:39:34
回答 1查看 820关注 0票数 17

考虑下面这段java 8代码:

代码语言:javascript
复制
public class Generics {
  public static <V, E extends Exception> V f(CheckedCallable1<V, E> callable) throws E {
    return callable.call();
  }
  public static <V, E extends Exception> V g(CheckedCallable2<V, E> callable) throws E {
    return callable.call();
  }
  public static void main(String[] args) {
    f(() -> 1);
    g(() -> 1);
  }
}

interface Callable<V> {
  V call() throws Exception;
}

interface CheckedCallable1<V, E extends Exception> {
  V call() throws E;
}

interface CheckedCallable2<V, E extends Exception> extends Callable<V> {
  @Override V call() throws E;
}

调用f时的lambda可以很好地编译,而调用g时的lambda不会编译,而是会给出这个编译错误:

代码语言:javascript
复制
Error:(10, 7) java: call() in <anonymous Generics$> cannot implement call() in CheckedCallable2
  overridden method does not throw java.lang.Exception

为什么会这样呢?

在我看来,CheckedCallable1.callCheckedCallable2.call方法是等价的:根据类型擦除规则,V变成了Object,因为它是无界的,而E变成了Exception,因为这是类型的上限。那么,为什么编译器认为被覆盖的方法不会抛出java.lang.Exception呢?

即使忽略类型擦除,这在这里可能是不相关的,因为这都是在编译时发生的,这对我来说仍然没有意义:我看不出为什么这种模式,如果允许,会导致,比方说,不健全的java代码。

所以有人能告诉我为什么这是不允许的吗?

更新:

所以我发现了一些更有趣的东西。使用上面的文件,将每次出现的Exception更改为IOException,并将throws子句添加为main。编译起作用了!改回Exception:编译中断!

这段代码编译得很好:

代码语言:javascript
复制
import java.io.IOException;

public class Generics {
  public static <V, E extends IOException> V f(CheckedCallable1<V, E> callable) throws E {
    return callable.call();
  }
  public static <V, E extends IOException> V g(CheckedCallable2<V, E> callable) throws E {
    return callable.call();
  }
  public static void main(String[] args) throws IOException {
    f(() -> 1);
    g(() -> 1);
  }
}

interface Callable<V> {
  V call() throws IOException;
}

interface CheckedCallable1<V, E extends IOException> {
  V call() throws E;
}

interface CheckedCallable2<V, E extends IOException> extends Callable<V> {
  @Override V call() throws E;
}

在这一点上,它开始看起来越来越像一个java bug……

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-06-14 01:31:36

我不认为有一条规则禁止这种模式。很可能你发现了一个编译器错误。

只需写下g(() -> 1);的等效内部类代码,就可以很容易地证明该模式不会导致不合理的代码

代码语言:javascript
复制
g(new CheckedCallable2<Integer, RuntimeException>() {
    public Integer call() {
        return 1;
    }
});

它的编译和运行没有任何问题,即使是在Java版本6下(我假设它甚至可以在Java版本5上运行,但我没有JDK来测试它),当使用lambda做同样的事情时,它没有理由不工作。将此代码写在Netbeans中会导致建议将其转换为lambda。

也没有运行时限制,这将禁止这样的构造。除了在幕后没有强制执行异常规则并且一切都依赖于编译时检查这一事实之外,我们甚至可以证明,如果编译器通过手动创建编译器将创建的代码来接受我们的代码,它将会工作:

代码语言:javascript
复制
CheckedCallable2<Integer,RuntimeException> c;
try
{
  MethodHandles.Lookup l = MethodHandles.lookup();
  c=(CheckedCallable2)
    LambdaMetafactory.metafactory(l, "call",
      MethodType.methodType(CheckedCallable2.class),
      MethodType.methodType(Object.class),
      l.findStatic(Generics.class, "lambda$1", MethodType.methodType(int.class)),
      MethodType.methodType(Integer.class)).getTarget().invokeExact();
} catch(Throwable t) { throw new AssertionError(t); }
int i=g(c);
System.out.println(i);
// verify that the inheritance is sound:
Callable<Integer> x=c;
try { System.out.println(x.call()); }// throws Exception
catch(Exception ex) { throw new AssertionError(ex); }

…
static int lambda$1() { return 1; }// the synthetic method for ()->1

这段代码按照预期运行并生成1,而不管我们使用哪个interface来执行call()。只有我们必须捕获的异常才是不同的。但如上所述,这是一个编译时工件。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24199148

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档