2015-06-10 3 views
22

Чтение JavaDoc Optional, я столкнулся с необычной сигнатурой метода; Я никогда не видел в своей жизни:throws x extends Сигнал метода исключения

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

На первый взгляд, я задавался вопросом, как родовое исключение <X extends Throwable> даже возможно, так как вы не можете сделать это (here и here). С другой стороны, это начинает иметь смысл, так как здесь просто привязать Supplier ... но сам поставщик точно знает, какой тип он должен быть, до дженериков.

Но вторая линия ударил меня:

  • throws X полный общий тип исключения.

И потом:

  • X extends Throwable, что в мире делает это значит?
    • X уже связан в методе подписи.
  • Будет ли это каким-либо образом решить универсальное ограничение исключения?
  • Почему не просто throws Throwable, так как остальные будут стерты стиранием типа?

И один, не связанные непосредственно вопрос:

  • Будет необходим этот метод, чтобы быть пойманным, как catch(Throwable t), или по типу представленной Supplier «s; поскольку он не может быть проверен во время выполнения?
+4

Вы не можете иметь общие типы «Исключения», но вы можете привязывать типы «Исключение» к общим параметрам. –

+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, который связан с Optional «s T
  • Мы ожидаем Supplier, который имеет подстановочный верхнюю границу X
  • Кидает X (который является действительным, так как X имеет верхнюю грань Throwable). Это указано в JLS 8.4.6; до тех пор, пока X рассматривается как подтип Throwable, его декларация действительна и легальна здесь.

There is an open bug о Javadoc вводящий в заблуждение. В этом случае лучше всего доверять исходному коду, а не документации, пока ошибка не будет объявлена ​​фиксированной.

А почему мы используем throws X вместо throws Throwable: X гарантированно будет обязан Throwable в самых верхних границ. Если вы хотите более конкретный Throwable (время выполнения, отмеченное или Error), то просто бросать Throwable не даст вам такой гибкости.

Для вашего последнего вопроса:

Будет необходим этот метод, чтобы быть пойманным в улове (Throwable т) п?

Что-то вниз по цепочке должно обработать исключение, то, что 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

Double подтвердил: Существует 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 не связан. Метод объявляет его границы.

Будет ли это каким-либо образом решать универсальное ограничение исключения?

No.

Почему просто не extends Throwable, а остальные будут удалены по типу стирания?

Generics - это функция времени компиляции. Erasure происходит после компиляции. Я считаю, что они могли бы просто использовать

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> 

catch и instanceof зависят от типа информации во время выполнения; стирание руин это.

Однако, это слишком жестко, чтобы запретить общее объявление типов исключений, таких как FooException<T>.Java может это позволить; просто требуют, что только сырье типа и подстановочные типа допускаются в catch

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

Аргумент может быть сделан, что тип исключения в основном интересовались улове пунктами; если мы не сможем поймать общие типы исключений, нет смысла иметь их в первую очередь. ОК.


Теперь, мы не можем сделать это либо

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

Проблема в том, КОД может бросать любой тип исключения; исходный шаблон бросает все, что бросает КОД; и мы хотим, чтобы выражение 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 или более типами исключений.


Хорошо, это действительно хорошо. Но мы не видим такого использования в новых API JDK. Никакие функциональные типы любого параметра метода не могут вызывать любое проверенное исключение. Если вы делаете Stream.map(x->{ BODY }), BODY не может выбрасывать какие-либо проверенные исключения. Они действительно ненавидят проверенные исключения с лямбдами.

Другим примером является CompletableFuture, где исключение является центральной частью всей концепции; Тем не менее, исключенные исключения не допускаются ни в лямбда-телах.

Если вы мечтатель, вы можете поверить, что в какой-то будущей версии Java будет изобретен элегантный механизм прозрачности исключений для лямбды, ничего подобного уродливому throws X hack; поэтому теперь нам не нужно загрязнять наши API с помощью <X>. Да, сон, человек.


Итак, сколько throws X используется в любом случае?

На протяжении всего источника JDK, единственный публичный API, который делает это Optional.orElseThrow() (и его кузены OptionalInt/Long/Double). Вот и все.

(Там же недостаток с orElseGet/orElseThrow дизайном. - решение разветвленности не может быть сделано в теле лямбда Мы могли бы вместо того, чтобы разработать более общий метод

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() во время выполнения; однако компилятор и среда выполнения не смогли его предотвратить. В основном виноват Erasure.

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

Смежные вопросы