2017-05-17 23 views
7

Với Java, để truy cập an toàn tham chiếu lồng nhau sâu như a.b.c.d.e, chúng tôi thường phải chỉ định kiểm tra rỗng ở mỗi cấp hoặc gói trong Optional s và sử dụng orElse().Cách thay thế cho Null Coalescing Operator

public <T> T valueOrNull(Supplier<T> expression) { 
    try { 
     return expression.get(); 
    } catch (NullPointerException e) { 
     return null; 
    } 
} 

này sau đó có thể được sử dụng một cách an toàn với value = valueOrNull(() -> a.b.c.d.e): (Không giống với các ngôn ngữ như Kotlin/C# nơi a?.b?.c?.d?.e hoặc các sản phẩm tương tự

Tôi đã tự hỏi nếu phương pháp helper sau đây có thể là một lựa chọn hợp lý..

Lưu ý: Tôi hiểu rằng việc bắt giữ NullPointerException s thường bị cau mày vì lý do hiệu suất và hơn thế nữa, nhưng tự hỏi liệu việc sử dụng ở đây có phải là một ngoại lệ hợp lý hay không.

+0

Việc nắm bắt 'NullPointerExceptions' có rất ít việc phải làm với hiệu suất và rất nhiều việc phải làm với chất lượng mã chung. – Kayaman

+0

Người ta thậm chí có thể lập luận rằng phải viết 'abcde' (hoặc' a? .b? .c? .d? .e') đã cho biết thiết kế không rõ ràng (không phải tôi sẽ không chào đón một nhà điều hành như vậy, nhưng tôi có xu hướng nhận được ít làm tổ hơn thế). – Kayaman

+0

@Kayaman Tôi đã đề cập đến hiệu suất vì sử dụng ngoại lệ cho luồng điều khiển thường chậm hơn nhiều so với phân nhánh đơn giản. Và chất lượng mã sẽ không phải là một vấn đề nếu điều này bị cô lập chỉ với phương pháp này. Về nhận xét thứ hai của bạn, điều này có thể đúng trong hầu hết các trường hợp, nhưng hãy xem xét một POJO đại diện cho một lược đồ XML, nơi bạn sử dụng JAXB hoặc một số cho chuyển đổi. Đó là hợp lệ để có quyền truy cập vào một nút xml lồng nhau sâu nơi bạn không thể chắc chắn về tính hợp lệ của đường dẫn. – Naufal

Trả lời

1

Bạn có thể viết một hàm mang theo một giá trị và một getter để biến nó, như vậy

public static <T1, T2> T1 coalesce(T2 value, Function<T2,T1> f1){ 
    return f1.apply(value); 
} 

mà sau đó bạn sẽ gọi như vậy coalesce(value, Clazz::getA).

Đối với mỗi bước xa hơn trong chuỗi của bạn, bạn cần một chức năng bổ sung trong chức năng liên hiệp của bạn, như vậy

cho độ sâu hai, và

public static <T1, T2, T3, T4> T1 coalesce(T4 value, Function<T4,T3> f1, Function<T3,T2> f2, Function<T2,T1> f3){ 
    T3 t3 = f1.apply(value); 
    if(t3 == null) 
     return null; 
    T2 t2 = f2.apply(t3); 
    if(t2 == null) 
     return null; 
    return f3.apply(t2); 
} 

cho độ sâu ba và cứ thế tiếp tục sâu hơn.

Ví dụ mã:

A test1 = new A(null); 
    A test2 = new A(new B(null)); 
    A test3 = new A(new B(new C(null))); 
    A test4 = new A(new B(new C("1234"))); 

    System.out.println(coalesce(test1, A::getB, B::getC, C::getS)); 
    System.out.println(coalesce(test2, A::getB, B::getC, C::getS)); 
    System.out.println(coalesce(test3, A::getB, B::getC, C::getS)); 
    System.out.println(coalesce(test4, A::getB, B::getC, C::getS)); 

    System.out.println(coalesce(test2, A::getB)); 
    System.out.println(coalesce(test3, A::getB, B::getC)); 

A là một lớp học với một thành viên của lớp B, B là một lớp học với một thành viên của lớp C, C là một lớp học với một thành viên của lớp String, với thu khí thích hợp . Đầu ra là như mong đợi:

null 
null 
null 
1234  
[email protected] 
[email protected] 

Nulls cho ba trường hợp đầu tiên, nơi mà các chuỗi trong C là null, thứ tư có giá trị của chuỗi, và các đối tượng trở lại thứ năm và thứ sáu của loại B và C respectivelly.

Tôi không thấy cách làm cho mã ngắn hơn nhiều.

+0

Giải pháp của OP chắc chắn linh hoạt hơn và đơn giản hơn. – Kayaman

+0

Giải pháp của OP cũng sử dụng các ngoại lệ cho luồng điều khiển và âm thầm bắt được ngoại lệ. Tôi chỉ đơn giản là thêm một cách khác để làm điều đó trong java 8. –

+0

"lặng lẽ" bắt ngoại lệ là một tính năng chứ không phải là một vấn đề tiêu cực ở đây. – Kayaman