2015-01-28 16 views
9

Các thử nghiệm sau thất bạiHai phương pháp tài liệu tham khảo chính xác không bằng nhau

@Test 
public void test() { 
    Function<String, Integer> foo = Integer::parseInt; 
    Function<String, Integer> bar = Integer::parseInt; 
    assertThat(foo, equalTo(bar)); 
} 

là có cách nào để làm cho nó vượt qua?

chỉnh sửa: Tôi sẽ cố gắng làm rõ hơn những gì tôi đang cố gắng làm.

phép nói rằng tôi có các lớp:

class A { 
    public int foo(Function<String, Integer> foo) {...} 
} 

class B { 
    private final A a; // c'tor injected 
    public int bar() { 
    return a.foo(Integer::parseInt); 
    } 
} 

bây giờ cho phép nói rằng tôi muốn viết bài kiểm tra đơn vị cho B:

@Test 
public void test() { 
    A a = mock(A.class); 
    B b = new B(a); 
    b.bar(); 
    verify(a).foo(Integer::parseInt); 
} 

vấn đề là những thử nghiệm thất bại, bởi vì các tài liệu tham khảo phương pháp là không công bằng.

+1

"điều này ngăn cản tôi viết một bài kiểm tra đơn vị cho một phương thức lấy hàm làm đối số" <- phỏng đoán đầu tiên: kiểm tra không kiểm tra những gì cần. Điều gì sẽ xảy ra nếu bạn nói thêm một chút về bài kiểm tra được đề cập? – fge

+0

Tôi sẽ giả định cách để kiểm tra rằng hai chức năng tương đương là để cho ăn công cụ vào chúng và kiểm tra cùng một điều đi ra ở phía bên kia. – biziclop

+1

@biziclop nhưng bạn sẽ cần phải kiểm tra toàn bộ tên miền của các giá trị đầu vào, và đôi khi bạn không thể làm điều đó: p – fge

Trả lời

11

Lambdas không được lưu trong bộ nhớ cache và điều này dường như có chủ ý. Không có cách nào để so sánh hai lambdas để xem nếu họ sẽ làm điều tương tự.

Bạn cần phải làm một cái gì đó giống như

static final Function<String, Integer> parseInt = Integer::parseInt; 

@Test 
public void test() { 
    Function<String, Integer> foo = parseInt; 
    Function<String, Integer> bar = parseInt; 
    assertThat(foo, equalTo(bar)); 
} 

trả lời từ Brian Goetz; Is there a way to compare lambdas?

4

Tôi không có API trong tầm tay nhưng chức năng là giao diện. Integer :: parseInt có vẻ không phải cache, vì vậy nó sẽ trả về hai cá thể khác nhau, sẽ được so sánh bằng tham chiếu => false.

Bạn có thể làm cho nó vượt qua bằng cách viết một Comparator, mà làm những gì bạn muốn.

+0

Và khi bạn nghĩ về nó, chúng có thể so sánh chúng như thế nào? – biziclop

+5

Tôi nghi ngờ rằng có bất kỳ thực hiện so sánh nào sẽ làm những gì OP muốn, theo một cách di động ít nhất. –

+0

@MarkoTopolnik Vâng, lý do chính là 'MethodHandle' không chỉ là các con trỏ phương pháp đơn giản, bạn có thể tạo các phương thức xử lý tổng hợp hoặc nhân tạo hoàn toàn không trỏ đến bất kỳ phương thức thực tế nào của một lớp thực. Và làm thế nào để bạn nói một xử lý của một phương thức không làm gì ngoài việc ném một 'UnsupportedOperationException' tương đương với một phương thức mà bạn đã tạo thông qua' MethodHandles.throwException() '? – biziclop

0

OK là thử nghiệm không vượt qua. Lambdas không phải là đối tượng, chúng không phải là đối tượng của các thuộc tính như nhận diện đối tượng. Thay vào đó, chúng là các triển khai adhoc của các giao diện chức năng.

Tôi tin rằng bạn không nên mong đợi mã của bạn dựa vào hành vi bạn đã mô tả.

1

Có nhìn vào ngôn ngữ Java Specification:

15.27.4. Run-time Evaluation of Lambda Expressions

Vào lúc chạy, đánh giá của một biểu thức lambda là tương tự như đánh giá của một biểu thức tạo lớp Ví dụ, trong chừng mực hoàn như bình thường tạo ra một tài liệu tham khảo cho một đối tượng. Việc đánh giá một biểu thức lambda khác với việc thực hiện cơ thể lambda.

Ví dụ mới của lớp có thuộc tính bên dưới được cấp phát và khởi tạo hoặc phiên bản hiện tại của lớp có thuộc tính bên dưới được tham chiếu.

...

Những quy định này có nghĩa là để cung cấp sự linh hoạt để triển khai của các ngôn ngữ lập trình Java, trong đó:

  • Một đối tượng mới không cần phải được phân bổ trên mỗi lần đánh giá.

  • Đối tượng được tạo bởi các biểu thức lambda khác nhau không cần thuộc các lớp khác nhau (ví dụ: nếu các vật thể giống nhau).

  • Mọi đối tượng được tạo ra bởi việc đánh giá không cần thuộc cùng một lớp (các biến cục bộ được ghi có thể được gạch chân, chẳng hạn).

  • Nếu một "phiên bản hiện có" có sẵn, nó không cần phải được tạo ở đánh giá lambda trước đó (ví dụ: có thể đã được cấp phát trong quá trình khởi tạo lớp kèm theo).

Về nguyên tắc, điều này có nghĩa là ngay cả một lần xuất hiện của Integer::parseInt trong mã nguồn của bạn có thể dẫn đến trường hợp đối tượng khác nhau (thậm chí của các tầng lớp khác nhau) khi đang được đánh giá nhiều lần, chứ không phải để nói chuyện nhiều lần xuất hiện của nó. Quyết định chính xác là để thực hiện JRE thực tế. Xem this answer thảo luận về hành vi hiện tại của việc triển khai của Oracle.

Các vấn đề liên quan