2012-01-23 29 views
7

Hãy nói rằng tôi có như sau:Tại sao việc triển khai giao diện chung này tạo ra một tham chiếu mơ hồ?

public interface Filter<E> { 
    public boolean accept(E obj); 
} 

import java.io.File; 
import java.io.FilenameFilter; 

public abstract class CombiningFileFilter extends javax.swing.filechooser.FileFilter 
     implements java.io.FileFilter, FilenameFilter { 

    @Override 
    public boolean accept(File dir, String name) { 
     return accept(new File(dir, name)); 
    } 
} 

Khi đứng, bạn có thể sử dụng javac để biên dịch CombiningFileFilter. Nhưng, nếu bạn cũng quyết định để thực hiện Filter<File> trong CombiningFileFilter, bạn nhận được lỗi sau:

CombiningFileFilter.java:9: error: reference to accept is ambiguous, 
both method accept(File) in FileFilter and method accept(E) in Filter match 
       return accept(new File(dir, name)); 
        ^
    where E is a type-variable: 
    E extends Object declared in interface Filter 
1 error 

Tuy nhiên, nếu tôi thực hiện một lớp thứ ba:

import java.io.File; 

public abstract class AnotherFileFilter extends CombiningFileFilter implements 
     Filter<File> { 
} 

Có không còn là một lỗi biên dịch. Các lỗi biên dịch cũng sẽ biến mất nếu Filter là không chung:

public interface Filter { 
    public boolean accept(File obj); 
} 

Tại sao có thể không phải là trình biên dịch hình ra rằng kể từ khi lớp thực hiện Filter<File>, phương pháp accept nên thực sự là accept(File) và rằng không có sự mơ hồ? Ngoài ra, tại sao lỗi này chỉ xảy ra với javac? (Nó hoạt động tốt với trình biên dịch của Eclipse.)

/chỉnh sửa
Một workaround sạch hơn đến vấn đề biên dịch này vì tạo lớp thứ ba sẽ có thêm phương pháp public abstract boolean accept(File) trong CombiningFileFilter. Điều đó xóa bỏ sự mơ hồ.

/e2
Tôi đang sử dụng JDK 1.7.0_02.

+0

thử xóa '@ Ghi đè ' –

+0

@HunterMcMillen Bạn vẫn gặp lỗi – Jeffrey

+0

Có một giao diện va chạm giữa' javax.swing.filechooser.FileFilter # accept (Tệp) 'và của riêng bạn' Bộ lọc #accept (Tệp) '. Xem tại đây để có giải thích chi tiết: http://stackoverflow.com/a/2832651/406984 – earldouglas

Trả lời

9

Theo như tôi có thể nói, các lỗi biên dịch được ủy thác của ngôn ngữ Java Specification, mà writes:

Let C be a class or interface declaration with formal type parameters A1,...,An , and let C<T1,...,Tn> be an invocation of C , where, for 1in, Ti are types (rather than wildcards). Then:

  • Let m be a member or constructor declaration in C, whose type as declared is T. Then the type of m (§8.2, §8.8.6) in the type C<T1,...,Tn> , is T[A1 := T1, ..., An := Tn] .
  • Let m be a member or constructor declaration in D, where D is a class extended by C or an interface implemented by C. Let D<U1,...,Uk> be the supertype of C<T1,...,Tn> that corresponds to D. Then the type of m in C<T1,...,Tn> is the type of m in D<U1,...,Uk> .

If any of the type arguments to a parameterized type are wildcards, the type of its members and constructors is undefined.

Đó là, phương pháp tuyên bố bởi Filter<File> đã gõ boolean accept(File). FileFilter cũng khai báo phương thức boolean accept(File).

CombiningFilterFilter kế thừa cả hai phương pháp này.

Điều đó có nghĩa là gì? Java Language Specification writes:

It is possible for a class to inherit multiple methods with override-equivalent (§8.4.2) signatures.

It is a compile time error if a class C inherits a concrete method whose signatures is a subsignature of another concrete method inherited by C.

(Điều đó không áp dụng, vì không phải phương pháp là bê tông.)

Otherwise, there are two possible cases:

  • If one of the inherited methods is not abstract, then there are two subcases:
    • If the method that is not abstract is static, a compile-time error occurs.
    • Otherwise, the method that is not abstract is considered to override, and therefore to implement, all the other methods on behalf of the class that inherits it. If the signature of the non-abstract method is not a subsignature of each of the other inherited methods an unchecked warning must be issued (unless suppressed (§9.6.1.5)). A compile-time error also occurs if the return type of the non-abstract method is not return type substitutable (§8.4.5) for each of the other inherited methods. If the return type of the non-abstract method is not a subtype of the return type of any of the other inherited methods, an unchecked warning must be issued. Moreover, a compile-time error occurs if the inherited method that is not abstract has a throws clause that conflicts (§8.4.6) with that of any other of the inherited methods.
  • If all the inherited methods are abstract, then the class is necessarily an abstract class and is considered to inherit all the abstract methods. A compile-time error occurs if, for any two such inherited methods, one of the methods is not return type substitutable for the other (The throws clauses do not cause errors in this case.)

Vì vậy, "sáp nhập" các phương pháp di truyền override tương đương thành một phương pháp duy nhất xảy ra nếu một trong số đó là cụ thể, nếu tất cả đều trừu tượng thì chúng vẫn riêng biệt, vì vậy tất cả chúng đều có thể truy cập và áp dụng cho lời gọi phương thức.

Java Language Specification defines là những gì xảy ra sau đó như sau:

If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.

The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.

Sau đó xác định cụ thể hơn chính thức. Tôi sẽ dành cho bạn định nghĩa, nhưng cần lưu ý rằng cụ thể hơn không phải là một phần đơn đặt hàng, vì mỗi phương pháp là cụ thể hơn so với chính nó. Sau đó nó viết:

A method m1 is strictly more specific than another method m2 if and only if m1 is more specific than m2 and m2 is not more specific than m1.

Vì vậy, trong trường hợp của chúng tôi, nơi chúng tôi có một số phương pháp có chữ ký giống hệt nhau, mỗi là cụ thể hơn hơn người kia, nhưng không phải là chặt chẽ cụ thể hơn hơn người kia.

A method is said to be maximally specific for a method invocation if it is accessible and applicable and there is no other method that is applicable and accessible that is strictly more specific.

Vì vậy, trong trường hợp của chúng tôi, tất cả được thừa hưởng accept phương pháp tối đa cụ thể.

If there is exactly one maximally specific method, then that method is in fact the most specific method; it is necessarily more specific than any other accessible method that is applicable. It is then subjected to some further compile-time checks as described in §15.12.3.

Đáng buồn thay, đó không phải là trường hợp ở đây.

It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:

  • If all the maximally specific methods have override-equivalent (§8.4.2) signatures, then:
    • If exactly one of the maximally specific methods is not declared abstract, it is the most specific method.
    • Otherwise, if all the maximally specific methods are declared abstract, and the signatures of all of the maximally specific methods have the same erasure (§4.6), then the most specific method is chosen arbitrarily among the subset of the maximally specific methods that have the most specific return type. However, the most specific method is considered to throw a checked exception if and only if that exception or its erasure is declared in the throws clauses of each of the maximally specific methods.
  • Otherwise, we say that the method invocation is ambiguous, and a compile-time error occurs.

Và cuối cùng, là điểm nổi bật: Tất cả các phương pháp kế thừa đều giống nhau và do đó có chữ ký ghi đè tương đương. Tuy nhiên, phương pháp được kế thừa từ giao diện chung Filter không có cùng xóa như các phương thức khác.

Do đó,

  1. Ví dụ đầu tiên sẽ biên dịch bởi vì tất cả các phương pháp có tính trừu tượng, ghi đè tương đương, và có tẩy xoá tương tự.
  2. Ví dụ thứ hai sẽ không biên dịch, vì tất cả các phương thức đều trừu tượng, ghi đè tương đương, nhưng việc xóa của chúng không giống nhau.
  3. Ví dụ thứ ba sẽ biên dịch, vì tất cả các phương pháp candicate đều trừu tượng, ghi đè tương đương và có cùng xóa. (Phương pháp có cách xóa khác nhau được khai báo trong phân lớp và do đó không phải là ứng viên)
  4. Ví dụ thứ tư sẽ biên dịch, vì tất cả các phương thức đều trừu tượng, ghi đè tương đương và có cùng xóa.
  5. Ví dụ cuối cùng (phương thức trừu tượng lặp lại trong CombiningFileFilter) sẽ biên dịch, vì phương thức đó ghi đè tương đương với tất cả các phương thức được thừa kế accept và do đó ghi đè chúng (lưu ý rằng không cần xóa cùng một giá trị để ghi đè!). Vì vậy, chỉ có một phương pháp có thể áp dụng và có thể truy cập được, do đó, cụ thể nhất là.

Tôi chỉ có thể suy đoán tại sao thông số kỹ thuật yêu cầu xóa tương tự ngoài ghi đè tương đương. Nó có thể là do, để giữ lại khả năng tương thích ngược với mã không chung, trình biên dịch được yêu cầu phát ra một phương thức tổng hợp với chữ ký bị xóa khi khai báo phương thức tham chiếu đến các tham số kiểu. Trong thế giới bị xóa này, trình biên dịch có thể sử dụng phương thức nào làm đích cho biểu thức gọi phương thức? Đặc tả Ngôn ngữ Java bên bước các vấn đề này bằng cách yêu cầu rằng một tuyên bố phương thức thích hợp, chia sẻ, xoá hoàn toàn có mặt.

Để kết luận, hành vi của javac, mặc dù cách xa trực quan, được yêu cầu bởi Đặc tả ngôn ngữ Java, và nhật thực thất bại trong việc kiểm tra tính tương thích.

+0

Câu trả lời cho câu hỏi là tại sao bản gốc không biên dịch. Tôi giả sử trình biên dịch Eclipse hoặc kết hợp tất cả các phương thức vào một hoặc chỉ tùy ý chọn một phương thức bởi vì như bạn đã nói, tất cả chúng đều được hợp nhất với nhau trong lớp bê tông. – Jeffrey

+0

Chờ đợi, không phải điều này có nghĩa là 'CombiningFileFilter mở rộng javax.swing.filechooser.FileFilter thực hiện java.io.FileFilter, FilenameFilter' không nên biên dịch? Cả hai 'java.io.FileFilter' và' javax.swing.filechooser.FileFilter' đều có các phương thức trừu tượng với chữ ký 'boolean accept (File)'. – Jeffrey

+0

Đào sâu hơn, cuối cùng tôi đã tìm thấy đoạn giải thích tất cả, và đã sửa đổi câu trả lời cho phù hợp. – meriton

1

Có một phương thức trong giao diện FileFilter có cùng chữ ký với giao diện từ giao diện cụ thể Filter<File> của bạn. Cả hai đều có chữ ký accept(File f).

Đây là một tham chiếu mơ hồ vì trình biên dịch không có cách nào biết được phương thức nào trong số các phương thức này để gọi trong cuộc gọi phương thức accept(File f, String name) bị ghi đè của bạn.

+0

Rất tiếc, tôi quên đề cập đến điều này chỉ xảy ra khi giao diện 'Bộ lọc' là chung. Nếu 'Filter' có phương thức' accept (File) 'thay vì' accept (E) ', thì không còn tham chiếu mơ hồ nữa. Và theo trình biên dịch, các chữ ký mơ hồ là 'accept (File)' và 'accept (E)'. Không phải là 'accept (File)' và 'accept (File)'. – Jeffrey

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