2015-03-02 31 views
4

我的一個項目是throwing-lambdas;其中我的目的是爲了緩解在Stream s中潛在@FunctionalInterface的使用,其唯一在流中使用的「缺陷」是它們拋出檢查的異常(就我而言,我寧願稱爲有缺陷的事實,即你不能拋出從流中檢查異常但這是另一回事)。在這種情況下,我如何處理函數<T, R>和省略號/可變參數?

所以,Function<T, R>我定義的:

@FunctionalInterface 
public interface ThrowingFunction<T, R> 
    extends Function<T, R> 
{ 
    R doApply(T t) 
     throws Throwable; 

    default R apply(T t) 
    { 
     try { 
      return doApply(t); 
     } catch (Error | RuntimeException e) { 
      throw e; 
     } catch (Throwable throwable) { 
      throw new ThrownByLambdaException(throwable); 
     } 
    } 
} 

這讓我來定義,比如:

final ThrowingFunction<Path, Path> = Path::toRealPath; 

(爲什麼Path::toRealPath ......好吧,precisely because it has an ellipsis)。

因爲不想停在這裏我希望能夠寫出這樣的東西:

Throwing.function(Path::toRealPath).fallbackTo(Path::toAbsolutePath) 

以上NEARLY工程...閱讀。

我還定義了這一點:

public abstract class Chainer<N, T extends N, C extends Chainer<N, T, C>> 
{ 
    protected final T throwing; 

    protected Chainer(final T throwing) 
    { 
     this.throwing = throwing; 
    } 

    public abstract C orTryWith(T other); 

    public abstract <E extends RuntimeException> T orThrow(
     final Class<E> exclass); 

    public abstract N fallbackTo(N fallback); 

    public final <E extends RuntimeException> T as(final Class<E> exclass) 
    { 
     return orThrow(exclass); 
    } 
} 

這是它Function S上的實現:

public final class ThrowingFunctionChain<T, R> 
    extends Chainer<Function<T, R>, ThrowingFunction<T, R>, ThrowingFunctionChain<T, R>> 
    implements ThrowingFunction<T, R> 
{ 
    public ThrowingFunctionChain(final ThrowingFunction<T, R> function) 
    { 
     super(function); 
    } 

    @Override 
    public R doApply(final T t) 
     throws Throwable 
    { 
     return throwing.doApply(t); 
    } 

    @Override 
    public ThrowingFunctionChain<T, R> orTryWith(
     final ThrowingFunction<T, R> other) 
    { 
     final ThrowingFunction<T, R> function = t -> { 
      try { 
       return throwing.doApply(t); 
      } catch (Error | RuntimeException e) { 
       throw e; 
      } catch (Throwable ignored) { 
       return other.doApply(t); 
      } 
     }; 

     return new ThrowingFunctionChain<>(function); 
    } 

    @Override 
    public <E extends RuntimeException> ThrowingFunction<T, R> orThrow(
     final Class<E> exclass) 
    { 

     return t -> { 
      try { 
       return throwing.doApply(t); 
      } catch (Error | RuntimeException e) { 
       throw e; 
      } catch (Throwable throwable) { 
       throw ThrowablesFactory.INSTANCE.get(exclass, throwable); 
      } 
     }; 
    } 

    @Override 
    public Function<T, R> fallbackTo(final Function<T, R> fallback) 
    { 
     return t -> { 
      try { 
       return doApply(t); 
      } catch (Error | RuntimeException e) { 
       throw e; 
      } catch (Throwable ignored) { 
       return fallback.apply(t); 
      } 
     }; 
    } 
} 

到目前爲止好(雖然IDEA fails to recognize the code of orTryWith() as valid,但那是另一回事)。

我還定義了一個名爲Throwing工具類的問題就出在這個班,我寫了一個測試的main():現在

public final class Throwing 
{ 
    private Throwing() 
    { 
     throw new Error("nice try!"); 
    } 

    public static <T, R> ThrowingFunctionChain<T, R> function(
     final ThrowingFunction<T, R> function) 
    { 
     return new ThrowingFunctionChain<>(function); 
    } 

    public static void main(final String... args) 
    { 
     // FAILS TO COMPILE 
     final Function<Path, Path> f = function(Path::toRealPath) 
      .fallbackTo(Path::toAbsolutePath); 
    } 
} 

,對於上面的代碼中的錯誤信息是:

Error:(29, 48) java: incompatible types: cannot infer type-variable(s) T,R 
    (argument mismatch; invalid method reference 
     method toRealPath in interface java.nio.file.Path cannot be applied to given types 
     required: java.nio.file.LinkOption[] 
     found: java.lang.Object 
     reason: varargs mismatch; java.lang.Object cannot be converted to java.nio.file.LinkOption) 
Error:(29, 49) java: invalid method reference 
    non-static method toRealPath(java.nio.file.LinkOption...) cannot be referenced from a static context 
Error:(30, 25) java: invalid method reference 
    non-static method toAbsolutePath() cannot be referenced from a static context 

我無法診斷錯誤的確切原因在這裏,但對我來說,它看起來像省略號阻礙了;其實,如果我這樣做:

final ThrowingFunctionChain<Path, Path> f = function(Path::toRealPath); 

    try (
     final Stream<Path> stream = Files.list(Paths.get("")); 
    ) { 
     stream.map(f.fallbackTo(Path::toAbsolutePath)) 
      .forEach(System.out::println); 
    } 

然後再編譯:所以這意味着Stream.map()不承認結果作爲一個Function ...

那麼爲什麼不會Throwing.function(Path::toRealPath).fallbackTo(Path::toAbsolutePath)編譯?

+0

只是一個建議:'公共接口ThrowingFunction 延伸功能 { řdoApply(T T) 拋出Ë;' – Bohemian 2015-03-03 03:53:27

+0

@Bohemian這種方法的問題是它不會對拋出多個異常的方法效果不佳;還有,你是剝奪了一些非常酷的潛在候選人的λ如'MethodHandle :: invoke' – fge 2015-03-03 15:09:59

回答

3

你的代碼片段

Function<Path, Path> f = function(Path::toRealPath).fallbackTo(Path::toAbsolutePath); 

更是創下其包含在規範的Java 8的類型推斷的限制,所以它不是一個編譯器錯誤。鏈式方法調用時,目標輸入不起作用。由於鏈的第一個方法是可變參數方法,因此需要其目標類型來查找預期的調用簽名。這種情況與編寫p->p.toRealPath()時類似,其中調用的參數數量是明確的,但p的類型未知。雙方將在不調用鏈工作(除了在最後調用)

這可以通過使第一次調用明確的類型來解決,

Function<Path, Path> f = Throwing.<Path,Path>function(Path::toRealPath) 
    .fallbackTo(Path::toAbsolutePath); 

ThrowingFunctionChain<Path, Path> f0 = function(Path::toRealPath); 
Function<Path, Path> f = f0.fallbackTo(Path::toAbsolutePath); 

Function<Path, Path> f = function((Path p)->p.toRealPath()) 
    .fallbackTo(Path::toAbsolutePath); 

或者通過將方法調用鏈轉換爲unchained方法invocati附件爲described here

public static <T, R> ThrowingFunctionChain<T, R> function(
    final ThrowingFunction<T, R> function) 
{ 
    return new ThrowingFunctionChain<>(function); 
} 
public static <T, R> Function<T, R> function(
    final ThrowingFunction<T, R> function, Function<T, R> fallBack) 
{ 
    return new ThrowingFunctionChain<>(function).fallbackTo(fallBack); 
} 
public static void main(final String... args) 
{ 
    Function<Path, Path> f = function(Path::toRealPath, Path::toAbsolutePath); 
} 

規範故意拒絕當一個調用針對其他結果兩個調用的類型推斷,但它的工作原理,如果同樣的表情只是另一次調用的參數。

4

正如Holger在comments,compiler is limited in its type inference when chaining methods中所述。只是提供了一個明確的類型參數

final Function<Path, Path> f = Throwing.<Path, Path>function(Path::toRealPath).fallbackTo(Path::toAbsolutePath); 
+1

是的,這的確是解決問題......而我認爲這是一個錯誤的也因爲類型可以正確推斷當在流的'.map()'中使用'但是在將結果賦值給一個變量時不能使用:( – fge 2015-03-03 01:05:31

+1

這不是一個錯誤,它是規範的限制[鏈式方法調用時類型推斷是有限的](http: //stackoverflow.com/a/26883991/2711488) – Holger 2015-03-03 10:58:53

+0

@Holger這是50/50在我的頭上。感謝您的指正。修憲...... – 2015-03-03 15:25:59

1

其中一個問題是,作爲一個可變參數的方法,Path::toRealPath具有用於目標類型推斷目的,以下不同類型(作爲隱式過載):

  • Path toRealPath(Path a) (一個new LinkOption[0]將作爲編譯器的第二個參數隱式提供)。
  • Path toRealPath(Path a,LinkOption... b)(第二個參數是直接的)。
  • Path toRealPath(Path a,LinkOption[] b)(第二個參數是直接的)。
  • Path toRealPath(Path a,LinkOption b)(第二個參數將由編譯器提供爲new LinkOption[1] { b })。
  • Path toRealPath(Path a,LinkOption b,LinkOption c)(第二個參數將由編譯器提供爲new LinkOption[2] { b, c })。
  • Path toRealPath(Path a,LinkOption b,LinkOption c,LinkOption d)(第二個參數會被編譯器作爲new LinkOption[3] { b, c, d }提供)
  • Path toRealPath(Path a,LinkOption b,LinkOption c,LinkOption d,LinkOption e)
  • etc(第二個參數會被編譯器爲new LinkOption[n] { b, c, d, e, ... }供給(第二個參數會被編譯器作爲new LinkOption[3] { b, c, d, e }提供) )

,另一種是通過解決陳述Function<Path,Path> f= function(Path::toRealPath).fallbackTo(Path::toAbsolutePath) ;隱含的型方程需要推斷類型參數fallbackTo所以它的返回類型符合Function<Path,Path>,然後輸入function的類型參數,使它自己的返回類型符合第一個。 Java使得這種推斷成爲可能,但只有當涉及單個步驟時(參數參數,返回值返回類型,從右到左分配)。在返回類型的情況下,推理鏈是無界的,通常有多個解決方案。

另一種方法是向編譯器提供更多類型的信息。例如:

Function<Path,Path> f= Throwing.<Path,Path>function(Path::toRealPath).fallbackTo(Path::toAbsolutePath) ; 

或通過創建臨時變量,如:

ThrowingFunctionChain<Path,Path> stage1= function(Path::toRealPath) ; 
Function<Path,Path> f= stage1.fallbackTo(Path::toAbsolutePath) ; 

在這種情況下stage1聲明提供的附加信息。

在更爲一般的情況下,我並不完全明白爲什麼人們希望在期待它們作爲規範時傳播異常。我已經做了或多或少的Optional<T>或通過使用一個非常小的擴展,它能夠封裝異常信息。您甚至可以使用1.7中引入的「抑制異常」機制來處理在對close方法的隱式調用期間發生的嘗試資源異常。問題似乎完全一樣。該代碼非常簡單,並且完全兼容streams和其他標準Java SE框架。

+0

「我不完全理解爲什麼人們會想期待時傳播異常他們作爲規範「< - 我做這個軟件包的原因是允許使用在流中使用的函數接口的」投擲版本「;不幸的是你不能從流中拋出檢查異常:( – fge 2015-03-03 15:09:10

+0

@fge是的,我聯合國derstand那一部分,但我要說的是,如果你傳播異常作爲例外,你會保持連連碰壁。更好地把它們封裝成一些與流更兼容(我敢說Java的,走的方向)。這是「可選」或其變體的類型。 – 2015-03-04 17:32:48

相關問題