ฟังก์ชั่น Java 8 Lambda ที่ส่งข้อยกเว้น?


469

ฉันรู้วิธีสร้างการอ้างอิงถึงวิธีการที่มีStringพารามิเตอร์และส่งกลับค่าเป็นint:

Function<String, Integer>

อย่างไรก็ตามวิธีนี้ใช้ไม่ได้หากฟังก์ชั่นมีข้อผิดพลาดสมมติว่ามันถูกกำหนดเป็น:

Integer myMethod(String s) throws IOException

ฉันจะกำหนดข้อมูลอ้างอิงนี้ได้อย่างไร


1
ที่เกี่ยวข้อง: stackoverflow.com/questions/31637892/ …
Marko Topolnik

1
... และอันนี้: stackoverflow.com/questions/31270759/…
Vadzim


4
วิธีแก้ปัญหาทั้งหมดดูเหมือนว่าจะทิ้งข้อยกเว้นรันไทม์ฉันเชื่อว่ามันไม่ใช่ทางออกที่ดี ดีกว่าที่จะใช้ java เก่าสำหรับลูป
Nazeel

5
แล้วห้องสมุดjoolล่ะ cf org.jooq.lambda. แพ็คเกจที่ไม่ได้ตรวจสอบ
chaiyachaiya

คำตอบ:


402

คุณจะต้องทำอย่างใดอย่างหนึ่งต่อไปนี้

  • หากเป็นรหัสของคุณให้กำหนดอินเทอร์เฟซการทำงานของคุณเองซึ่งจะประกาศข้อยกเว้นที่เลือก:

    @FunctionalInterface
    public interface CheckedFunction<T, R> {
       R apply(T t) throws IOException;
    }

    และใช้มัน:

    void foo (CheckedFunction f) { ... }
  • มิฉะนั้นให้ตัดInteger myMethod(String s)คำด้วยวิธีที่ไม่ประกาศข้อยกเว้นที่เลือก:

    public Integer myWrappedMethod(String s) {
        try {
            return myMethod(s);
        }
        catch(IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    แล้ว:

    Function<String, Integer> f = (String t) -> myWrappedMethod(t);

    หรือ:

    Function<String, Integer> f =
        (String t) -> {
            try {
               return myMethod(t);
            }
            catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        };

7
คุณสามารถขยายConsumerหรือFunctionถ้าคุณใช้วิธีการเริ่มต้น - ดูคำตอบของฉันด้านล่าง
jlb

2
ฉันคิดว่านี้สามารถทำได้เป็นหนึ่งซับ
เน็ด Twigg

6
การปรับให้เหมาะสมเล็กน้อย: แทนที่จะ(String t) -> myWrappedMethod(t)ใช้การอ้างอิงวิธีการthis::myWrappedMethodก็ได้
Clashsoft

8
วิธีทั่วไปยิ่งกว่านั้นคือการกำหนดฟังก์ชันที่ถูกตรวจสอบเช่น @FunctionalInterface public interface CheckedFunction <T, R, E ขยายข้อยกเว้น> {R ใช้ (T t) พ่น E; } ด้วยวิธีนี้คุณยังสามารถกำหนดข้อยกเว้นว่าฟังก์ชันกำลังขว้างปาและสามารถใช้อินเทอร์เฟซสำหรับรหัสใด ๆ
Martin Odhelius

3
ว้าว. Java แย่กว่าที่ฉันคิดไว้
user275801

194

คุณสามารถขยายConsumer(และFunctionอื่น ๆ ) ด้วยอินเทอร์เฟซใหม่ที่จัดการข้อยกเว้น - โดยใช้วิธีการเริ่มต้นของ Java 8 !

พิจารณาอินเทอร์เฟซนี้ (ขยายConsumer):

@FunctionalInterface
public interface ThrowingConsumer<T> extends Consumer<T> {

    @Override
    default void accept(final T elem) {
        try {
            acceptThrows(elem);
        } catch (final Exception e) {
            // Implement your own exception handling logic here..
            // For example:
            System.out.println("handling an exception...");
            // Or ...
            throw new RuntimeException(e);
        }
    }

    void acceptThrows(T elem) throws Exception;

}

ตัวอย่างเช่นถ้าคุณมีรายการ:

final List<String> list = Arrays.asList("A", "B", "C");

หากคุณต้องการที่จะกินมัน (เช่นด้วยforEach) ด้วยรหัสบางอย่างที่ส่งข้อยกเว้นคุณจะตั้งค่าบล็อกลอง / catch แบบดั้งเดิม:

final Consumer<String> consumer = aps -> {
    try {
        // maybe some other code here...
        throw new Exception("asdas");
    } catch (final Exception ex) {
        System.out.println("handling an exception...");
    }
};
list.forEach(consumer);

แต่ด้วยอินเทอร์เฟซใหม่นี้คุณสามารถยกตัวอย่างได้ด้วยนิพจน์แลมบ์ดาและคอมไพเลอร์จะไม่บ่น:

final ThrowingConsumer<String> throwingConsumer = aps -> {
    // maybe some other code here...
    throw new Exception("asdas");
};
list.forEach(throwingConsumer);

หรือแม้แต่เพียงแค่โยนให้สั้นลง!:

list.forEach((ThrowingConsumer<String>) aps -> {
    // maybe some other code here...
    throw new Exception("asda");
});

อัปเดต : ดูเหมือนว่ามีส่วนไลบรารียูทิลิตี้ที่ดีมากของทุเรียนที่เรียกว่าข้อผิดพลาดซึ่งสามารถใช้ในการแก้ปัญหานี้ได้อย่างยืดหยุ่นมากขึ้น ตัวอย่างเช่นในการใช้งานของฉันด้านบนฉันได้กำหนดนโยบายการจัดการข้อผิดพลาด ( System.out...หรือthrow RuntimeException) อย่างชัดเจนในขณะที่ข้อผิดพลาดของทุเรียนช่วยให้คุณสามารถใช้นโยบายได้ทันทีผ่านวิธีการใช้งานชุดใหญ่ ขอขอบคุณที่แบ่งปัน @NedTwigg!

ตัวอย่างการใช้งาน:

list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));

14
ดังนั้นคุณจึงมีชุดอินเทอร์เฟซ (ฟังก์ชันผู้บริโภคผู้จำหน่าย ... ) และชุดนโยบายสำหรับการจัดการข้อผิดพลาด (การขว้างปา System.out.println, ... ) ฉันคิดว่ามีวิธีที่จะทำให้การใช้นโยบายใด ๆ กับฟังก์ชั่นประเภทใด ๆ เป็นเรื่องง่ายโดยไม่ต้องคัดลอกวาง "ThrowingConsumer, ThrowingFunction ฯลฯ "
เน็ด Twigg

1
ในเวลาต่อมา ... ฉันตัดสินใจใช้ข้อยกเว้นที่ไม่ได้ตรวจสอบและไม่ใช้อินเทอร์เฟซการทำงานเพิ่มเติมหรือห้องสมุดใหม่ -> ถนนง่ายพิมพ์น้อยลงส่งเร็วขึ้นใช่มั้ย
aliopi

1
นี่คือรุ่นที่ปรับปรุงโดยใช้สำนวนส่อเสียด ไม่จำเป็นต้องแกะ RuntimeException เข้าไปใน CheckException
myui

61

ฉันคิดว่าชั้นเรียนของทุเรียนErrorsรวมข้อดีข้อเสนอแนะต่าง ๆ ไว้มากมาย

หากต้องการรวมทุเรียนในโครงการของคุณคุณสามารถ:

  • คว้ามันจากjcenterหรือmaven central atcom.diffplug.durian:durian:3.3.0
  • หรือเพียงแค่คัดลอกวางเพียงสองชั้นเรียนขนาดเล็กลงในรหัสของคุณ: Throwing.javaและErrors.java

หรือคุณสามารถใช้RxJavaเนื่องจากสตรีมต้องการการจัดการข้อผิดพลาดโดยธรรมชาติและหากมีบางอย่างในไพพ์ไลน์ของคุณที่ส่งข้อยกเว้นมีโอกาสที่ดีมันอาจเป็นสตรีมที่สังเกตได้ สิ่งนี้ไม่ได้บังคับให้ Java 8 ใช้งานผู้ใช้ดาวน์สตรีมของไลบรารี
Adam Gent

2
โปรดทราบว่าทุเรียนไม่มีรุ่นใหม่ตั้งแต่เดือนมิถุนายน 2559 ไม่ใช่การหยุดการแสดง แต่เป็นสิ่งที่ต้องคำนึงถึง
Istvan

9
ผู้ดูแลทุเรียนที่นี่ เกิดอะไรขึ้น หากผู้ใช้พบข้อบกพร่องหรือคุณลักษณะที่สำคัญขาดหายไปเราจะปล่อยตัวแก้ไขข้อบกพร่องอย่างรวดเร็ว ห้องสมุดเป็นเรื่องง่ายเราจึงไม่ได้มีรายงานข้อผิดพลาดใด ๆ ดังนั้นเราไม่จำเป็นต้องปล่อยข้อผิดพลาดใด ๆ
เน็ด Twigg

28

นี่ไม่ใช่เฉพาะของ Java 8 คุณกำลังพยายามรวบรวมสิ่งที่เทียบเท่ากับ:

interface I {
    void m();
}
class C implements I {
    public void m() throws Exception {} //can't compile
}

15
คำถามคือ"ฉันจะกำหนดข้อมูลอ้างอิงนี้ได้อย่างไร" . นี่ไม่ได้ตอบคำถามจริงๆ มันแค่ชี้แจงว่าปัญหาคืออะไร
Dawood ibn Kareem

13

คำเตือน: ฉันยังไม่ได้ใช้ Java 8 แต่อ่านได้เท่านั้น

Function<String, Integer>ไม่โยนIOExceptionดังนั้นคุณไม่สามารถใส่รหัสใด ๆ ลงไปthrows IOExceptionได้ หากคุณกำลังเรียกวิธีการที่คาดว่าจะเป็นFunction<String, Integer>แล้วแลมบ์ดาที่คุณส่งผ่านไปยังวิธีการที่ไม่สามารถโยนIOExceptionระยะเวลา คุณสามารถเขียนแลมบ์ดาได้เช่นนี้ (ฉันคิดว่านี่เป็นไวยากรณ์แลมบ์ดาไม่แน่ใจ):

(String s) -> {
    try {
        return myMethod(s);
    } catch (IOException ex) {
        throw new RuntimeException(ex);
        // (Or do something else with it...)
    }
}

หรือถ้าวิธีการที่คุณส่งแลมบ์ดาเป็นวิธีที่คุณเขียนด้วยตัวคุณเองคุณสามารถกำหนดอินเทอร์เฟซการทำงานใหม่และใช้มันเป็นประเภทพารามิเตอร์แทนFunction<String, Integer>:

public interface FunctionThatThrowsIOException<I, O> {
    O apply(I input) throws IOException;
}

เพิ่มคำอธิบายประกอบ @FunctionalInterface ก่อนอินเทอร์เฟซของคุณเท่านั้นจากนั้นจะสามารถใช้งานได้สำหรับ lambdas
Gangnus

13
@Gangnus: @FunctionalInterfaceคำอธิบายประกอบนั้นไม่จำเป็นสำหรับการใช้งาน lambdas ขอแนะนำสำหรับการตรวจสุขภาพ
Tanmay Patil

9

หากคุณไม่รังเกียจที่จะใช้ lib บุคคลที่สาม ( Vavr ) คุณสามารถเขียนได้

CheckedFunction1<String, Integer> f = this::myMethod;

นอกจากนี้ยังมี Try monad ที่เรียกว่าซึ่งจัดการข้อผิดพลาด:

Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable)
        .map(i -> ...) // only executed on Success
        ...

โปรดอ่านเพิ่มเติม ที่นี่

ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้สร้าง Vavr



6

อย่างไรก็ตามคุณสามารถสร้าง FunctionInterface ของคุณเองที่พ่นด้านล่าง ..

@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
  void accept(T instance) throws X;
}

จากนั้นดำเนินการโดยใช้ Lambdas หรือการอ้างอิงตามที่แสดงด้านล่าง

import java.io.FileWriter;
import java.io.IOException;

//lambda expressions and the execute around method (EAM) pattern to
//manage resources

public class FileWriterEAM  {
  private final FileWriter writer;

  private FileWriterEAM(final String fileName) throws IOException {
    writer = new FileWriter(fileName);
  }
  private void close() throws IOException {
    System.out.println("close called automatically...");
    writer.close();
  }
  public void writeStuff(final String message) throws IOException {
    writer.write(message);
  }
  //...

  public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {

    final FileWriterEAM writerEAM = new FileWriterEAM(fileName);    
    try {
      block.accept(writerEAM);
    } finally {
      writerEAM.close();
    }
  }

  public static void main(final String[] args) throws IOException {

    FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));

    FileWriterEAM.use("eam2.txt", writerEAM -> {
        writerEAM.writeStuff("how");
        writerEAM.writeStuff("sweet");      
      });

    FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);     

  }


 void writeIt() throws IOException{
     this.writeStuff("How ");
     this.writeStuff("sweet ");
     this.writeStuff("it is");

 }

}

6

คุณสามารถ.

การขยาย @marcg UtilExceptionและเพิ่ม generic <E extends Exception>เมื่อจำเป็น: ด้วยวิธีนี้คอมไพเลอร์จะบังคับให้คุณอีกครั้งเพื่อเพิ่มส่วนคำสั่งการขว้าง

public final class LambdaExceptionUtil {

    @FunctionalInterface
    public interface Function_WithExceptions<T, R, E extends Exception> {
        R apply(T t) throws E;
    }

    /**
     * .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName))
     */
    public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E  {
        return t -> {
            try {
                return function.apply(t);
            } catch (Exception exception) {
                throwActualException(exception);
                return null;
            }
        };
    }

    @SuppressWarnings("unchecked")
    private static <E extends Exception> void throwActualException(Exception exception) throws E {
        throw (E) exception;
    }

}

public class LambdaExceptionUtilTest {

    @Test
    public void testFunction() throws MyTestException {
        List<Integer> sizes = Stream.of("ciao", "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
        assertEquals(2, sizes.size());
        assertEquals(4, sizes.get(0).intValue());
        assertEquals(5, sizes.get(1).intValue());
    }

    private Integer transform(String value) throws MyTestException {
        if(value==null) {
            throw new MyTestException();
        }
        return value.length();
    }

    private static class MyTestException extends Exception { }
}

5

ฉันมีปัญหานี้กับ Class.forName และ Class.newInstance ภายในแลมบ์ดาดังนั้นฉันเพิ่งทำ:

public Object uncheckedNewInstanceForName (String name) {

    try {
        return Class.forName(name).newInstance();
    }
    catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

ภายในแลมบ์ดาแทนที่จะเรียก Class.forName ("myClass") newInstance () ฉันเพิ่งเรียกว่าไม่ถูกตรวจสอบ newInstanceForName ("myClass")


4

วิธีแก้ปัญหาอื่นที่ใช้ฟังก์ชั่น wrapper ก็คือการคืนค่าอินสแตนซ์ของ wrapper ของผลลัพธ์ของคุณพูดว่า Success ถ้าทุกอย่างเป็นไปด้วยดีไม่ว่าจะเป็นอินสแตนซ์ของ Failure

รหัสบางอย่างเพื่อชี้แจงสิ่ง:

public interface ThrowableFunction<A, B> {
    B apply(A a) throws Exception;
}

public abstract class Try<A> {

    public static boolean isSuccess(Try tryy) {
        return tryy instanceof Success;
    }

    public static <A, B> Function<A, Try<B>> tryOf(ThrowableFunction<A, B> function) {
        return a -> {
            try {
                B result = function.apply(a);
                return new Success<B>(result);
            } catch (Exception e) {
                return new Failure<>(e);
            }
        };
    }

    public abstract boolean isSuccess();

    public boolean isError() {
        return !isSuccess();
    }

    public abstract A getResult();

    public abstract Exception getError();
}

public class Success<A> extends Try<A> {

    private final A result;

    public Success(A result) {
        this.result = result;
    }

    @Override
    public boolean isSuccess() {
        return true;
    }

    @Override
    public A getResult() {
        return result;
    }

    @Override
    public Exception getError() {
        return new UnsupportedOperationException();
    }

    @Override
    public boolean equals(Object that) {
        if(!(that instanceof Success)) {
            return false;
        }
        return Objects.equal(result, ((Success) that).getResult());
    }
}

public class Failure<A> extends Try<A> {

    private final Exception exception;

    public Failure(Exception exception) {
        this.exception = exception;
    }

    @Override
    public boolean isSuccess() {
        return false;
    }

    @Override
    public A getResult() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Exception getError() {
        return exception;
    }
}

กรณีใช้งานง่าย:

List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
    map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
    collect(Collectors.toList());

4

ปัญหานี้ก็ทำให้ฉันรำคาญเช่นกัน; นี่คือเหตุผลที่ฉันได้สร้างโครงการนี้

ด้วยคุณสามารถทำ:

final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;

มีอินเทอร์เฟซทั้งหมด 39 อินเตอร์เฟสที่กำหนดโดย JDK ซึ่งมีคุณสมบัติThrowingเทียบเท่ากัน ผู้ที่มีทั้งหมด@FunctionalInterfaceของใช้ในลำธาร (ฐานStreamแต่ยังIntStream, LongStreamและDoubleStream )

และในขณะที่แต่ละคนขยายขอบเขตการขว้างปาไม่ใช่คุณสามารถใช้พวกมันโดยตรงในลูกแกะเช่นกัน:

myStringStream.map(f) // <-- works

พฤติกรรมเริ่มต้นคือเมื่อการขว้างปาแลมบ์ดาของคุณโยนข้อยกเว้นที่ตรวจสอบแล้ว ThrownByLambdaExceptionจะถูกโยนโดยมีข้อยกเว้นที่ตรวจสอบว่าเป็นสาเหตุ คุณสามารถจับภาพนั้นและรับสาเหตุ

คุณสมบัติอื่น ๆ ก็มีให้เช่นกัน


ฉันชอบความคิดนี้มากฉันหวังว่าคุณจะสร้างบททั่วไปตามที่แนะนำไว้ที่นี่: javaspecialists.eu/archive/Issue221.htmlเช่น: @FunctionalInterface public interface SupplierWithCE<T, X extends Exception> { T get() throws X; }- วิธีนี้ผู้ใช้ไม่จำเป็นต้องจับThrowableแต่เป็นข้อยกเว้นเฉพาะในการตรวจสอบแทน
Zoltán

@ Zoltan ที่จะเจ็บปวดที่จะประกาศข้อยกเว้นทุกครั้งแม้ว่า; นอกจากนี้คุณยังสามารถใช้พูดใช้. () แทน. doApply () และจับได้ThrownByLambdaExceptionเสมอคุณจะมีข้อยกเว้นดั้งเดิมเป็นสาเหตุ (หรือคุณสามารถใช้rethrow(...).as(MyRuntimeException.class))
fge

ฉันคิดว่ามันมีวิธี (ประมาณ )
เน็ด Twigg

@NedTwigg ฉันได้แก้ไขมานานแล้วเช่นกัน; ตอนนี้ฉันสามารถใช้Throwing.runnable()และคนอื่น ๆ พร้อมความสามารถในการผูกมัด
fge

ฟังก์ชั่นผูกมัดนั้นยอดเยี่ยมมาก! ความคิดเห็นของฉันเกี่ยวกับ ThrowingRunnable ควรมีข้อยกเว้นทั่วไปหรือไม่ โซลทันถามว่าห้องสมุดของคุณอาจมีข้อโต้แย้งเป็นพารามิเตอร์ทั่วไปหรือไม่และคุณบอกว่าอาจเป็นเรื่องยากที่จะใช้ ลิงก์ของฉันคือรหัสบางบรรทัดที่แสดงวิธีหนึ่งที่จะมีข้อยกเว้นทั่วไปโดยไม่ต้องเจ็บปวด ถ้าฉันไม่ได้อ่านมันผิดข้อยกเว้นในไลบรารีของคุณไม่ใช่แบบทั่วไป (ซึ่งเป็นตัวเลือกการออกแบบที่สมเหตุสมผล
เน็ด Twigg

4

มีคำตอบที่ยอดเยี่ยมมากมายที่โพสต์แล้วที่นี่ เพียงแค่พยายามที่จะแก้ปัญหาด้วยมุมมองที่แตกต่าง มันเป็นเพียง 2 เซ็นต์ของฉันโปรดแก้ไขฉันถ้าฉันผิดที่อื่น

พ่นประโยคใน FunctionalInterface ไม่ใช่ความคิดที่ดี

ฉันคิดว่านี่อาจไม่ใช่ความคิดที่ดีในการบังคับใช้การพ่น IOException เนื่องจากเหตุผลต่อไปนี้

  • นี่ดูเหมือนว่าฉันจะมีรูปแบบต่อต้านการสตรีม / แลมบ์ดา แนวคิดทั้งหมดคือผู้โทรจะตัดสินใจว่าจะให้รหัสใดและจะจัดการข้อยกเว้นอย่างไร ในหลายสถานการณ์ IOException อาจไม่สามารถใช้ได้กับไคลเอนต์ ตัวอย่างเช่นหากลูกค้าได้รับค่าจากแคช / หน่วยความจำแทนการทำ I / O จริง

  • นอกจากนี้ข้อยกเว้นในการจัดการสตรีมก็น่าเกลียดน่ากลัวจริงๆ ตัวอย่างเช่นนี่คือรหัสของฉันจะดูเหมือนว่าฉันใช้ API ของคุณ

               acceptMyMethod(s -> {
                    try {
                        Integer i = doSomeOperation(s);
                        return i;
                    } catch (IOException e) {
                        // try catch block because of throws clause
                        // in functional method, even though doSomeOperation
                        // might not be throwing any exception at all.
                        e.printStackTrace();
                    }
                    return null;
                });

    น่าเกลียดใช่ไหม ยิ่งกว่านั้นดังที่ฉันได้กล่าวถึงในจุดแรกของฉันว่าวิธี doSomeOperation อาจหรืออาจไม่ได้รับการโยน IOException (ขึ้นอยู่กับการใช้งานของลูกค้า / ผู้โทร) แต่เนื่องจากข้อประโยคในวิธีการของคุณ FunctionInterface ฉันมักจะต้องเขียน ลองจับ

ฉันต้องทำอย่างไรถ้าฉันรู้ว่า API นี้พ่น IOException จริงๆ

  • ดังนั้นบางทีเราอาจจะสับสนอินเตอร์เฟซอินเตอร์เฟซกับอินเทอร์เฟซทั่วไป หากคุณรู้ว่า API นี้จะใช้งาน IOException ส่วนใหญ่คุณอาจทราบถึงพฤติกรรมเริ่มต้น / นามธรรมเช่นกัน ฉันคิดว่าคุณควรกำหนดอินเทอร์เฟซและปรับใช้ไลบรารีของคุณ (ด้วยการใช้งานเริ่มต้น / นามธรรม) ดังต่อไปนี้

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    }

    แต่ปัญหา catch-catch ยังคงมีอยู่สำหรับลูกค้า หากฉันใช้ API ของคุณในสตรีมฉันยังคงต้องจัดการ IOException ในบล็อก try-catch ที่น่าเกลียด

  • ระบุ API ที่เป็นมิตรต่อสตรีมเริ่มต้นดังต่อไปนี้

    public interface MyAmazingAPI {
        Integer myMethod(String s) throws IOException;
    
        default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) {
            try {
                return Optional.ofNullable(this.myMethod(s));
            } catch (Exception e) {
                if (exceptionConsumer != null) {
                    exceptionConsumer.accept(e);
                } else {
                    e.printStackTrace();
                }
            }
    
            return Optional.empty();
        }
    }

    วิธีการเริ่มต้นใช้วัตถุผู้บริโภคเป็นอาร์กิวเมนต์ซึ่งจะรับผิดชอบในการจัดการข้อยกเว้น ตอนนี้จากมุมมองของลูกค้ารหัสจะมีลักษณะเช่นนี้

    strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
                    .filter(Optional::isPresent)
                    .map(Optional::get).collect(toList());

    ใช่มั้ย แน่นอนว่าสามารถใช้ตัวบันทึกหรือตรรกะการจัดการอื่น ๆ แทน Exception :: printStackTrace

  • นอกจากนี้คุณยังสามารถสัมผัสวิธีการคล้ายกับ https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function- หมายความว่าคุณสามารถเปิดเผยวิธีอื่นซึ่งจะมีข้อยกเว้นจากการเรียกวิธีการก่อนหน้านี้ ข้อเสียคือตอนนี้คุณกำลังทำให้ API ของคุณเป็นสถานะซึ่งหมายความว่าคุณต้องจัดการกับความปลอดภัยของเธรดและสิ่งนั้นจะกลายเป็นที่นิยมในที่สุด เพียงตัวเลือกที่ต้องพิจารณา


ฉันยอมรับว่าการแปลงข้อยกเว้นที่เลือกไปเป็นข้อยกเว้นที่ไม่มีการตรวจสอบหรือการกลืนข้อยกเว้นไม่ใช่ความคิดที่ดีเพราะไม่มีวิธีที่จะทราบว่าองค์ประกอบใดของStreamข้อยกเว้นที่ยกขึ้น ดังนั้นฉันชอบความคิดของการมีตัวจัดการข้อยกเว้นและกรองผลลัพธ์ที่ไม่ถูกต้อง โปรดทราบว่า MyAmazingAPI ของคุณมีประสิทธิภาพFunctionalInterface(ดังนั้นคุณสามารถเพิ่มคำอธิบายประกอบ @FunctionalInterface) Optional.empty()นอกจากนี้คุณอาจมีค่าเริ่มต้นแทนการใช้
Julien Kronegg

4

สำนวนการโยนส่อเสียดเปิดใช้งานCheckedExceptionการแสดงออกของแลมบ์ดา ห่อCheckedExceptionในRuntimeExceptionไม่ดีสำหรับจัดการข้อผิดพลาดที่เข้มงวด

มันสามารถใช้เป็นConsumerฟังก์ชั่นที่ใช้ในคอลเลกชัน Java

นี่คือการที่ง่ายและรุ่นปรับปรุงของคำตอบของจิ๊บ

import static Throwing.rethrow;

@Test
public void testRethrow() {
    thrown.expect(IOException.class);
    thrown.expectMessage("i=3");

    Arrays.asList(1, 2, 3).forEach(rethrow(e -> {
        int i = e.intValue();
        if (i == 3) {
            throw new IOException("i=" + i);
        }
    }));
}

เพียงแค่นี้ก็ wrapps แลมบ์ดาในrethrow มันทำให้CheckedExceptionสิ่งExceptionที่ขว้างทิ้งในแลมบ์ดาของคุณ

public final class Throwing {
    private Throwing() {}

    @Nonnull
    public static <T> Consumer<T> rethrow(@Nonnull final ThrowingConsumer<T> consumer) {
        return consumer;
    }

    /**
     * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it
     * allows the unchecked exception to propagate.
     * 
     * http://www.baeldung.com/java-sneaky-throws
     */
    @SuppressWarnings("unchecked")
    @Nonnull
    public static <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E {
        throw (E) ex;
    }

}

ค้นหารหัสที่สมบูรณ์และหน่วยทดสอบที่นี่


3

คุณสามารถใช้ETสำหรับสิ่งนี้ ET เป็นไลบรารี Java 8 ขนาดเล็กสำหรับการแปลง / แปลยกเว้น

ด้วย ET ดูเหมือนว่านี้:

// Do this once
ExceptionTranslator et = ET.newConfiguration().done();

...

// if your method returns something
Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t));

// if your method returns nothing
Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t));

ExceptionTranslatorอินสแตนซ์เป็นเธรดที่ปลอดภัยและสามารถแชร์ได้หลายองค์ประกอบ คุณสามารถกำหนดค่ากฎการแปลงข้อยกเว้นที่เฉพาะเจาะจงมากขึ้น (เช่นFooCheckedException -> BarRuntimeException) หากคุณต้องการ ถ้าไม่มีกฎระเบียบอื่น ๆ RuntimeExceptionที่มีข้อยกเว้นการตรวจสอบจะถูกแปลงโดยอัตโนมัติ

(ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียน ET)


2
ดูเหมือนว่าคุณเป็นผู้แต่งห้องสมุดนี้ ตามกฎของ SOคุณต้องเปิดเผยความร่วมมือในคำตอบของคุณ โปรดเพิ่มคำตอบของคุณอย่างชัดเจนว่าคุณได้เขียนไลบรารีนี้ (เหมือนกันสำหรับคำตอบที่เกี่ยวข้องกับ ET อื่น ๆ )
Tagir Valeev

2
สวัสดี Tagir ขอบคุณสำหรับคำใบ้ ฉันอัพเดตคำตอบแล้ว
มิชา

2

สิ่งที่ฉันทำคือการอนุญาตให้ผู้ใช้ให้คุณค่าที่เขาต้องการจริงในกรณีที่มีข้อยกเว้น ดังนั้นฉันจึงมีลักษณะเช่นนี้

public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) {
    return x -> {
        try {
            return delegate.apply(x);
        } catch (Throwable throwable) {
            return defaultValue;
        }
    };
}

@FunctionalInterface
public interface FunctionThatThrows<T, R> {
    R apply(T t) throws Throwable;
}

และสิ่งนี้สามารถเรียกเช่น:

defaultIfThrows(child -> child.getID(), null)

1
นี่คือส่วนขยายของความคิดนี้ซึ่งสร้างความแตกต่างระหว่างกลยุทธ์ "ค่าเริ่มต้น" (เช่นเดียวกับคำตอบของคุณ) และกลยุทธ์ "สร้างใหม่ RuntimeException" ซึ่งไม่จำเป็นต้องมีค่าเริ่มต้น
เน็ด Twigg

2

หากคุณไม่สนใจการใช้ห้องสมุดบุคคลที่สามซึ่งมีcyclops-reactห้องสมุดที่ฉันมีส่วนร่วมคุณสามารถใช้FluentFunctions API เพื่อเขียน

 Function<String, Integer> standardFn = FluentFunctions.ofChecked(this::myMethod);

ofChecked ใช้jOOλ CheckedFunction และส่งคืนการอ้างอิงที่อ่อนนุ่มกลับไปเป็นมาตรฐาน (ไม่ถูกตรวจสอบ) JDK java.util.function.Function

หรือคุณสามารถทำงานกับฟังก์ชั่นที่บันทึกไว้ได้ผ่านทาง FluentFunctions api!

ตัวอย่างเช่นในการดำเนินการวิธีการของคุณลองใหม่ได้ถึง 5 ครั้งและเข้าสู่ระบบเป็นสถานะที่คุณสามารถเขียน

  FluentFunctions.ofChecked(this::myMethod)
                 .log(s->log.debug(s),e->log.error(e,e.getMessage())
                 .try(5,1000)
                 .apply("my param");

2

ตามค่าเริ่มต้นฟังก์ชัน Java 8 ไม่อนุญาตให้มีข้อยกเว้นและตามที่เสนอในคำตอบหลายคำตอบมีหลายวิธีในการบรรลุผลทางเดียวคือ

@FunctionalInterface
public interface FunctionWithException<T, R, E extends Exception> {
    R apply(T t) throws E;
}

กำหนดเป็น:

private FunctionWithException<String, Integer, IOException> myMethod = (str) -> {
    if ("abc".equals(str)) {
        throw new IOException();
    }
  return 1;
};

และเพิ่มthrowsหรือtry/catchข้อยกเว้นเดียวกันในวิธีการโทร


2

สร้างประเภทส่งคืนแบบกำหนดเองที่จะเผยแพร่ข้อยกเว้นที่ตรวจสอบ นี่เป็นอีกทางเลือกหนึ่งในการสร้างอินเทอร์เฟซใหม่ที่สะท้อนถึงส่วนต่อประสานการทำงานที่มีอยู่ด้วยการปรับเปลี่ยนเล็กน้อยของ "throws exception" ในวิธีการของส่วนต่อประสานการทำงาน

คำนิยาม

CheckedValueSupplier

public static interface CheckedValueSupplier<V> {
    public V get () throws Exception;
}

CheckedValue

public class CheckedValue<V> {
    private final V v;
    private final Optional<Exception> opt;

    public Value (V v) {
        this.v = v;
    }

    public Value (Exception e) {
        this.opt = Optional.of(e);
    }

    public V get () throws Exception {
        if (opt.isPresent()) {
            throw opt.get();
        }
        return v;
    }

    public Optional<Exception> getException () {
        return opt;
    }

    public static <T> CheckedValue<T> returns (T t) {
        return new CheckedValue<T>(t);
    }

    public static <T> CheckedValue<T> rethrows (Exception e) {
        return new CheckedValue<T>(e);
    }

    public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            return Result.rethrows(e);
        }
    }

    public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) {
        try {
            return CheckedValue.returns(sup.get());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

การใช้

//  Don't use this pattern with FileReader, it's meant to be an
//  example.  FileReader is a Closeable resource and as such should
//  be managed in a try-with-resources block or in another safe
//  manner that will make sure it is closed properly.

//  This will not compile as the FileReader constructor throws
//  an IOException.
    Function<String, FileReader> sToFr =
        (fn) -> new FileReader(Paths.get(fn).toFile());

// Alternative, this will compile.
    Function<String, CheckedValue<FileReader>> sToFr = (fn) -> {
        return CheckedValue.from (
            () -> new FileReader(Paths.get("/home/" + f).toFile()));
    };

// Single record usage
    // The call to get() will propagate the checked exception if it exists.
    FileReader readMe = pToFr.apply("/home/README").get();


// List of records usage
    List<String> paths = ...; //a list of paths to files
    Collection<CheckedValue<FileReader>> frs =
        paths.stream().map(pToFr).collect(Collectors.toList());

// Find out if creation of a file reader failed.
    boolean anyErrors = frs.stream()
        .filter(f -> f.getException().isPresent())
        .findAny().isPresent();

เกิดอะไรขึ้น?

อินเทอร์เฟซที่ใช้งานได้เดียวที่สร้างข้อยกเว้นที่เลือกจะถูกสร้างขึ้น ( CheckedValueSupplier) นี่จะเป็นอินเทอร์เฟซที่ใช้งานได้เพียงตัวเดียวซึ่งอนุญาตให้มีข้อยกเว้นที่ตรวจสอบได้ ส่วนต่อประสานการทำงานอื่น ๆ ทั้งหมดจะใช้ประโยชน์จากCheckedValueSupplierตัดรหัสใด ๆ ที่ส่งข้อยกเว้นที่ตรวจสอบ

CheckedValueชั้นจะถือผลมาจากการดำเนินการตรรกะใด ๆ ที่พ่นยกเว้นการตรวจสอบ สิ่งนี้จะป้องกันการแพร่กระจายของข้อยกเว้นที่ตรวจสอบจนกว่าจะถึงจุดที่โค้ดพยายามเข้าถึงค่าที่อินสแตนซ์ของCheckedValueมี

ปัญหาเกี่ยวกับวิธีการนี้

  • ตอนนี้เรากำลังขว้าง "ยกเว้น" อย่างมีประสิทธิภาพซ่อนประเภทเฉพาะที่ถูกโยนไป
  • เราไม่ทราบว่ามีข้อยกเว้นเกิดขึ้นจนกว่าCheckedValue#get()จะมีการเรียก

ผู้บริโภคและคณะ

อินเตอร์เฟซที่ใช้งานได้บางอย่าง (Consumerตัวอย่าง) ต้องจัดการในลักษณะที่แตกต่างกันเนื่องจากไม่มีค่าตอบแทน

ฟังก์ชั่นแทนผู้บริโภค

วิธีหนึ่งคือการใช้ฟังก์ชั่นแทนการใช้งานของผู้บริโภคซึ่งใช้เมื่อจัดการกระแสข้อมูล

    List<String> lst = Lists.newArrayList();
// won't compile
lst.stream().forEach(e -> throwyMethod(e));
// compiles
lst.stream()
    .map(e -> CheckedValueSupplier.from(
        () -> {throwyMethod(e); return e;}))
    .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior

บานปลาย

RuntimeExceptionหรือคุณสามารถขยายไปยัง มีคำตอบอื่น ๆ ที่ครอบคลุมการยกระดับของข้อยกเว้นที่ตรวจสอบจากภายในConsumerที่ปกเพิ่มของการตรวจสอบข้อยกเว้นจากภายในเป็น

อย่ากิน

เพียงหลีกเลี่ยงอินเทอร์เฟซที่ใช้งานได้ทั้งหมดพร้อมกันและใช้รูปลักษณ์ที่ดีสำหรับการวนซ้ำ


2

ฉันใช้ฟังก์ชั่นยูทิลิตี้โอเวอร์โหลดที่เรียกว่าunchecked()ซึ่งจัดการหลายกรณีใช้งาน


การใช้ EAMPLE บางครั้ง

unchecked(() -> new File("hello.txt").createNewFile());

boolean fileWasCreated = unchecked(() -> new File("hello.txt").createNewFile());

myFiles.forEach(unchecked(file -> new File(file.path).createNewFile()));

การสนับสนุนสิ่งอำนวยความสะดวก

public class UncheckedUtils {

    @FunctionalInterface
    public interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingSupplier<T> {
        T get() throws Exception;
    }

    @FunctionalInterface
    public interface ThrowingRunnable {
        void run() throws Exception;
    }

    public static <T> Consumer<T> unchecked(
            ThrowingConsumer<T> throwingConsumer
    ) {
        return i -> {
            try {
                throwingConsumer.accept(i);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        };
    }

    public static <T> T unchecked(
            ThrowingSupplier<T> throwingSupplier
    ) {
        try {
            return throwingSupplier.get();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public static void unchecked(
            ThrowingRunnable throwing
    ) {
        try {
            throwing.run();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

0

โซลูชันที่นำเสนอจำนวนมากใช้อาร์กิวเมนต์ทั่วไปของ E เพื่อส่งผ่านประเภทของข้อยกเว้นที่เกิดขึ้น

ดำเนินการอย่างหนึ่งขั้นตอนต่อไปและแทนที่จะส่งผ่านประเภทของข้อยกเว้นส่งผ่านประเภทของข้อยกเว้นเช่นเดียวกับใน Consumer ...

Consumer<E extends Exception>

คุณอาจสร้างรูปแบบที่ใช้งานได้หลายรูปแบบConsumer<Exception>ซึ่งจะครอบคลุมความต้องการการจัดการข้อยกเว้นทั่วไปของแอปพลิเคชันของคุณ


0

ฉันจะทำสิ่งทั่วไป:

public interface Lambda {

    @FunctionalInterface
    public interface CheckedFunction<T> {

        T get() throws Exception;
    }

    public static <T> T handle(CheckedFunction<T> supplier) {
        try {
            return supplier.get();
        } catch (Exception exception) {
            throw new RuntimeException(exception);

        }
    }
}

การใช้งาน:

 Lambda.handle(() -> method());

0

ใช้Jool LibraryหรือพูดjOOλ libraryจากJOOQจากมันไม่เพียง แต่ให้ข้อยกเว้นการจัดการอินเทอร์เฟซที่ไม่ จำกัด แต่ยังให้ชั้น Seq ด้วยวิธีการที่มีประโยชน์มากมาย

นอกจากนี้ยังมีฟังก์ชั่นอินเทอร์เฟซที่มีพารามิเตอร์สูงสุด 16 พารามิเตอร์ นอกจากนี้ยังมีคลาส Tuple ซึ่งใช้ในสถานการณ์ที่แตกต่างกัน

Jool Git Link

โดยเฉพาะในการค้นหาไลบรารีสำหรับorg.jooq.lambda.fi.util.functionแพ็คเกจ มันมีส่วนต่อประสานทั้งหมดจาก Java-8 ที่มี Checked prepended ดูด้านล่างสำหรับการอ้างอิง: -

ป้อนคำอธิบายรูปภาพที่นี่


0

ฉันเขียน lib เล็ก ๆ ที่มีบางมายากลทั่วไปที่จะโยนใดก็ได้ยกเว้น Java โดยไม่จำเป็นต้องจับพวกเขามิได้ห่อไว้ในRuntimeException

การใช้งาน: unchecked(() -> methodThrowingCheckedException())

public class UncheckedExceptions {

    /**
     * throws {@code exception} as unchecked exception, without wrapping exception.
     *
     * @return will never return anything, return type is set to {@code exception} only to be able to write <code>throw unchecked(exception)</code>
     * @throws T {@code exception} as unchecked exception
     */
    @SuppressWarnings("unchecked")
    public static <T extends Throwable> T unchecked(Exception exception) throws T {
        throw (T) exception;
    }


    @FunctionalInterface
    public interface UncheckedFunction<R> {
        R call() throws Exception;
    }

    /**
     * Executes given function,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @return result of function
     * @see #unchecked(Exception)
     */
    public static <R> R unchecked(UncheckedFunction<R> function) {
        try {
            return function.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }


    @FunctionalInterface
    public interface UncheckedMethod {
        void call() throws Exception;
    }

    /**
     * Executes given method,
     * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception.
     *
     * @see #unchecked(Exception)
     */
    public static void unchecked(UncheckedMethod method) {
        try {
            method.call();
        } catch (Exception e) {
            throw unchecked(e);
        }
    }
}

แหล่งที่มา: https://github.com/qoomon/unchecked-exceptions-java


-7
public void frankTest() {
    int pageId= -1;

    List<Book> users= null;
    try {
        //Does Not Compile:  Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"), "", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name")));

        //Compiles:
        Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { 
            try {
                final Book bk= users.stream().filter(bp -> { 
                    String name= null;
                    try {
                        name = rw.getString("name");
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return bp.getTitle().equals(name); 
                }).limit(1).collect(Collectors.toList()).get(0);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return new Portal(rw.getInt("id"), "", users.get(0), rw.getString("name")); 
        } );
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

3
ต้องการแสดงความคิดเห็นงานของคุณ? คำตอบที่ใช้รหัสเท่านั้นจึงไม่มีประโยชน์
Phantômaxx

@ Franky คุณสามารถแก้ไขงานนำเสนอของคุณโดยใช้ระยะห่าง 4 แทน<code>/<code>:)
AdrieanKhisbe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.