2015-06-10 74 views
22

读取Optional的JavaDoc,我碰到了一个奇怪的方法签名;我从来没有在我的生活中看到:抛出x扩展异常方法签名

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) 
           throws X extends Throwable 

乍一看,我想知道一般异常<X extends Throwable>如何,甚至有可能,因为你不能做到这一点(herehere)。第二个想法是,这开始有意义,因为它只是为了绑定Supplier ...但供应商本身知道它应该是什么类型,在仿制药之前。

但第二线打我:

  • throws X是一个完整的通用的异常类型。

然后:

  • X extends Throwable,什么世界呢是什么意思?
    • X已经绑定在方法签名中。
  • 请问这是以任何方式解决一般的例外限制吗?
  • 为什么不只是throws Throwable,因为其余的将被类型擦除擦除?

和一个,不直接相关的问题:

  • 将需此方法被捕获作为catch(Throwable t),或者通过所提供的Supplier类型;因为它不能在运行时检查?
+4

不能有通用的'Exception'类型,但可以将'Exception'类型绑定到泛型参数。 –

+0

另外,这不是源代码声明,它是javadoc中的一些混淆(边界)。 –

+0

@SotiriosDelimanolis我已经指出了。 – Mordechai

回答

16

对待它就像你读过的任何其他通用代码。

这里的正式签署从我在Java 8's source code看到:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X 
  • X有一个上限的Throwable。这在以后很重要。
  • 我们返回一个类型T这势必OptionalT
  • 我们预计Supplier其中有上限的X
  • 我们扔X(这是有效的通配符,因为X有一个上限的Throwable )。这在JLS 8.4.6;只要X被视为Throwable的子类型,其声明在此处是合法且合法的。

There is an open bug关于Javadoc的误导。在这种情况下,最好相信源代码而不是文档,直到bug被声明为固定。

至于为什么我们使用throws X代替throws ThrowableX保证在最上面的边界绑定到Throwable。如果你想要一个更具体的Throwable(运行时间,选中或Error),那么仅仅投掷Throwable不会给你这种灵活性。

要你最后一个问题:

将需要使用此方法在catch(的Throwable t)的条款被抓?

东西向下链具有处理异常,是一个try...catch块或JVM本身。理想情况下,人们希望创建一个能够最好地表达他们需求的异常的Supplier。你不必(并且可能应该而不是)为这种情况创建一个catch(Throwable t);如果您的Supplier类型绑定到您需要处理的特定异常,那么最好将其用作链接后面的catch

+0

我在我的最后一颗子弹中犯了一个小错字,请检查它。 – Mordechai

+0

此后又增加了另一个问题。 – Mordechai

+2

请谨慎**关于在问题中添加多个问题。我已经解决了先前的担忧,我很乐意回顾修订后的项目符号,但第二个问题是过度的。稍后我会添加该部分。 – Makoto

10

Javadoc的方法签名是不同于源代码。按照b132 source

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) 
       throws X { // Added by me: See? No X extends Throwable 
    if (value != null) { 
     return value; 
    } else { 
     throw exceptionSupplier.get(); 
    } 
} 

证实:这与Javadoc生成的问题。作为测试,我创建了下面的类新项目和生成的Javadoc:

package com.stackoverflow.test; 

public class TestJavadoc { 
    public static <X extends Throwable> void doSomething() throws X { 

    } 
} 

这是造成的Javadoc:

Javadoc Screenshot

双人证实:有一个open bug about the Javadoc for this class

3

泛型类型和泛型变量/参数之间有区别。

泛型类型是声明泛型参数的类型。例如

class Generic<T> {} 

什么是不允许的是一个通用类型(如上),也延伸Throwable

class Generic <T> extends Throwable {} // nono 

这是在Java语言规范表达,here

这是一个编译时错误,如果一个泛型类是直接或间接的 子类Throwable(§11.1.1)。

但是,通用变量本身可以具有任何绑定,泛型或其他方式,只要它是引用类型即可。

X extends Throwable

意味着任何类型的结合(未具有边界)在编译时X必须是Throwable的子类型。

X已经绑定在方法签名中。

X未绑定。该方法声明其边界。

请问这是以任何方式解决一般异常限制吗?

为什么不extends Throwable,因为其余的将通过类型擦除被删除?

泛型是一个编译时功能。编译后发生擦除。我相信他们可以简单地使用

Supplier<? extends Throwable> 

作为参数类型。但是你会失去在throws子句中使用的类型变量。

进行后期编辑:

为什么不throws Throwable,因为其余的将通过类型擦除被删除?

您希望throws使用相同Exception类型的Supplier用品。

是否需要将此方法应用于catch(Throwable t)子句中?

否/取决于。如果您将检查的异常绑定到X,那么您需要以某种方式处理它。否则,不需要catch。但是最终会有/应该处理它,即使它是JVM本身。

+0

我在最后一个项目符号中犯了一个小错字,请检查它。 – Mordechai

+0

又增加了另一个问题,此后 – Mordechai

+1

@MouseEvent更新。 –

8

的Java不能赶上通用的异常类型,由于擦除

catch(FooException<Bar> e)  --analogy->  o instanceof List<String> 

catchinstanceof依赖于运行时类型信息;擦除废墟。

但是,禁止像FooException<T>这样的异常类型的泛型声明有点过于苛刻。Java可以允许它;只是要求只有原始类型和通配符类型被允许在catch

catch(FooException e)    --analogy->  o instanceof List 
    catch(FooException<?> e)        o instanceof List<?> 

的参数可以作出,异常的类型主要是由catch子句兴趣;如果我们无法捕捉到泛型异常类型,那么首先将它们排除在外是没有意义的。好。现在


,我们不能做到这一点无论

catch(X e) // X is a type variable 

那么为什么Java的允许X在首位抛出?

... foo() throws X 

正如我们后面会看到的,这个构造在擦除的辅助下实际上可以颠覆类型系统。

嗯,这个功能虽然 - 至少在概念上是非常有用的。我们编写泛型方法,以便我们可以为未知输入和返回类型编写模板代码;相同的投掷类型逻辑!例如,

<X extends Exception> void logAndThrow(X ex) throws X 
     ... 
     throw ex; 

    ... 
    (IOException e){ 
     logAndThrow(e); // throws IOException 

我们需要能够一般性地在抽象代码块时实现异常透明。另外一个例子,假设我们已经厌倦了写这种样板反复

lock.lock(); 
    try{ 
     CODE 
    }finally{ 
     lock.unlock(); 
    } 

的,我们要为一个包装方法,以在拉姆达的代码

Util.withLock(lock,()->{ CODE }); 

的问题是,代码可能抛出任何异常类型;原始的样板抛出任何CODE抛出;并且我们希望writeLock()表达式可以执行相同的操作,即异常透明度。解决方案 -

interface Code<X extends Throwable> 
    { 
     void run() throws X; 
    } 



    <X extends Throwable> void withLock(Lock lock, Code<X> code) throws X 
     ... 
     code.run(); // throws X 

    ... 
    withLock(lock,()->{ throws new FooException(); }); // throws FooException 

这只对检查的异常很重要。无论如何,未经检查的异常都可以自由传播。

throws X方法的一个严重缺陷是它不适用于2个或更多的异常类型。


好吧,那真的很好。但是我们在新的JDK API中没有看到这样的用法。没有任何方法参数的功能类型可以抛出任何检查的异常。如果你这样做Stream.map(x->{ BODY })BODY不能抛出任何检查的异常。他们真的很讨厌用lambdas检查异常。

另一个例子是CompletableFuture,其中异常是整个概念的中心部分;但是,在lambda体中不允许检查异常。

如果你是一个梦想家,你可能会相信在未来的某个版本的Java中,将会发明一种优雅的lambda异常透明机制,没有像这样丑陋的throws X黑客;因此我们现在不需要用<X> s污染我们的API。是的,梦想,男人。


那么,throws X用了多少呢?

在整个JDK源代码中,唯一公开的API是Optional.orElseThrow()(及其堂兄OptionalInt/Long/Double)。而已。

(有与orElseGet/orElseThrow设计一个缺点 - 分支的决定不能在lambda体内完毕后,我们可以改为设计一个更常用的方法

T orElse(lambda) throws X 

    optional.orElse(()->{ return x; }); 
    optional.orElse(()->{ throw ex; }); 
    optional.orElse(()->{ if(..)return x; else throw ex; }); 

此外orElseThrow,在JDK中唯一的其他方法throws X是非公ForkJoinTask.uncheckedThrow()它用于“偷偷摸摸扔”,即代码可以抛出检查异常,而编译器和运行不知情的情况 -

void foo() // no checked exception declared in throws 
    { 
     throw sneakyThrow(new IOExcepiton()); 
    } 

这里,IOException在运行时从foo()丢弃;但编译器和运行时未能阻止它。擦除主要是责备。

public static RuntimeException sneakyThrow(Throwable t) 
{ 
    throw Util.<RuntimeException>sneakyThrow0(t); 
} 
@SuppressWarnings("unchecked") 
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T 
{ 
    throw (T)t; 
} 
+0

您的回答非常实用,谢谢。 – Mordechai