2009-10-23 25 views
47

Tôi có một nhóm lớp thực hiện một giao diện chung: Command.Bắt buộc một lớp học ghi đè phương thức .equals

Và nhóm lớp học này vào Bản đồ.

Để làm cho Bản đồ hoạt động chính xác, tôi cần đến từng lớp thực hiện Lệnh để ghi đè phương thức Object.equals(Object other).

không sao.

Nhưng tôi không muốn ép buộc ghi đè bằng. => Có một lỗi biên dịch khi một cái gì đó thực hiện lệnh override không bằng.

Điều đó có thể xảy ra không?

Edit: BTW, tôi cũng sẽ cần phải buộc ghi đè hashcode ...

Trả lời

74

Không, bạn không thể. Những gì bạn có thể làm, tuy nhiên, là sử dụng một lớp cơ sở trừu tượng thay vì một giao diện, và làm cho equals() trừu tượng:

abstract class Command { 
    // put other methods from Command interface here 

    public abstract boolean equals(Object other); 
    public abstract int hashCode(); 
} 

lớp con của Commandphải sau đó cung cấp bình đẳng của mình và phương pháp hashCode.

Thực tiễn không tốt là buộc người dùng API phải mở rộng một lớp cơ sở nhưng có thể được chứng minh trong trường hợp này. Ngoài ra, nếu bạn tạo Command lớp cơ sở trừu tượng thay vì giao diện, thay vì giới thiệu lớp cơ sở nhân tạo trong bổ sung vào giao diện Lệnh thì không có nguy cơ người dùng API của bạn nhận sai.

+14

Bạn có thể muốn thêm vào một 'hashCode' trừu tượng, chỉ để hoàn thành. – McDowell

+0

Khá đúng ... chỉnh sửa cho phù hợp – skaffman

+0

hoạt động như một nét duyên dáng, và quả thực, cộng với tôi đã có một lớp cơ sở trừu tượng .... vì vậy, nó không phải là một sự tái cấu trúc lớn. Cảm ơn –

0

Kể từ equals() được thừa hưởng từ Object tôi nghi ngờ bạn có thể không thực sự buộc rằng, kể từ khi cho mỗi loại có một triển khai tự động được thừa kế của equals() khả dụng.

17

Bạn có thể mở rộng đối tượng của mình từ XObject trừu tượng thay vì java.lang.Object không?

public abstract class XObject 
extends Object 
{ 
@Override 
public abstract boolean equals(Object o); 
} 
+2

Tôi rất ngạc nhiên, nhưng điều này thực sự hiệu quả. – Jorn

+1

Không có gì đảm bảo rằng việc triển khai Command sẽ mở rộng lớp này. – skaffman

+0

Đây là những gì tôi sẽ đăng ban đầu :-) –

1

Điều này chỉ có thể xảy ra nếu Command là giao diện hoặc lớp trừu tượng, trong đó bằng (..) là phương thức được khai báo là trừu tượng.

Vấn đề là đối tượng, là siêu lớp của tất cả các đối tượng, đã xác định phương thức này.

Nếu bạn muốn cho biết đây là sự cố (vào thời gian chạy), bạn có thể ném ngoại lệ, buộc người dùng API của bạn ghi đè lên. Nhưng nó không thể ở thời gian biên dịch, ít nhất là kiến ​​thức của tôi.

Cố gắng giải quyết vấn đề này bằng cách có phương pháp cụ thể cho API, ví dụ: CommandEquals. Tùy chọn khác là (như đã đề cập) mở rộng một lớp khác định nghĩa một phương thức trừu tượng Equals.

+0

Nó chỉ hoạt động cho các lớp trừu tượng (như được mô tả bởi Pierre), không cho các giao diện. Bạn không phải triển khai phương thức equals được khai báo trong giao diện, vì nó đã có trong đối tượng của bạn. – Jorn

+0

Đọc lại câu thứ hai của tôi. –

0

Tôi không nghĩ rằng có thể ép buộc bằng bằng khi nó đến từ lớp Object.

Trên ghi chú liên quan, lưu ý rằng bạn cần ghi đè phương thức 'hashCode' từ lớp Object khi bạn ghi đè bằng. Điều này trở nên rất quan trọng nếu bạn định sử dụng các thể hiện của các lớp của bạn như là chìa khóa của một Bản đồ.Kiểm tra bài viết này: http://www.artima.com/lejava/articles/equality.html cung cấp một số gợi ý về cách ghi đè bằng một cách chính xác

0

Như các câu trả lời khác đã giải thích, không bạn không thể ép buộc loại điều bạn đang cố gắng.

Một điều có thể hoạt động 'đủ' là xác định giao diện thứ hai, gọi giao diện này là MappableCommand.

public interface MappableCommand 
{ 

} 

Trong tài liệu của bạn cho giao diện này cho biết một lớp chỉ nên triển khai giao diện trống (nếu trống) này đã xem xét các yêu cầu bạn đã nêu.

Sau đó, bạn có thể đặt loại Giá trị của bản đồ thành MappableCommand và chỉ MappableCommands mới có thể được thêm vào bản đồ.

Điều này tương tự như logic đằng sau lý do tại sao người ta cần triển khai giao diện (trống) Có thể tuần tự hóa cho các lớp có thể được tuần tự hóa bởi các cơ chế tuần tự mặc định của Java.

Nếu điều này không hiệu quả, thì bạn có thể phải giải quyết để ném lỗi thời gian chạy;

Fake Edit:

Nếu bạn muốn thực hiện yêu cầu này rõ ràng hơn bạn có thể định nghĩa giao diện mới theo cách này

public interface MappableCommand 
{ 

    public void iOverrodeTheEqualsMethod(); 

    public void seriouslyIPromiseThatIOverrodeIt(); 

} 
0

Bạn có thể tạo boolean myEquals() trong interface Command, và tạo Adaptor như thế này:

class MyAdapter{ 
    Command c; 
    boolean equals(Object x) { 
    return c.myEquals((Command)x); 
    } 
} 

Sau đó, bạn chỉ cần sử dụng map.put(key, new MyAdapter(command)) thay vì map.put(key, command)

0

Dưới đây là một biến thể của một số các giải pháp đề xuất khác:

public abstract class CommandOverridingEquals implements Command { 
    public abstract boolean equals(Object other); 
    public abstract int hashcode(); 
} 

Map<String, CommandOverridingEquals> map = 
    new HashMap<String, CommandOverridingEquals>(); 

Hoặc nếu bạn thực sự muốn để chắc chắn, sử dụng một hashmap kiểm tra; ví dụ.

Map<String, CommandOverridingEquals> map = Collections.checkedMap(
    new HashMap<String, Command>(), 
    String.class, CommandOverridingEquals.class); 

Nhưng không có vấn đề gì bạn làm bạn không thể ngăn người làm điều này:

public class AntiFascistCommand extends CommandOverridingEquals { 
    public boolean equals(Object other) { return super.equals(other); } 
    public int hashcode() { return super.hashcode(); } 
    ... 
} 

tôi có xu hướng nghĩ rằng loại điều sẽ gây ra rắc rối xuống theo dõi. Ví dụ, giả sử rằng tôi có một loạt các lớp lệnh hiện có mở rộng một số lớp cơ sở khác, và (ngẫu nhiên) ghi đè equalshashcode theo cách được quy định. Vấn đề là, tôi không thể sử dụng những lớp đó. Thay vào đó, tôi buộc phải reimplement chúng hoặc viết một bó wrappers.

IMO, bạn nên cố gắng buộc nhà phát triển đưa vào một mẫu triển khai cụ thể. Nó sẽ là tốt hơn để đưa một số cảnh báo mạnh mẽ vào Javadocs và dựa vào các nhà phát triển để làm điều đúng.

+0

Chắc chắn, nhưng nếu developper bỏ lỡ .equals override. Nó sẽ là một mớ hỗn độn trong một số bản đồ. Và khó tìm ra. Tôi chấp nhận rủi ro. –

1
interface A{ 
    public boolean equal2(Object obj); 
} 

abstract class B implements A { 

    @Override 
    public boolean equals(Object obj) { 
     return equal2(obj); 
    } 

} 


class C extends B { 

    public boolean equal2(Object obj) { 
     throw new UnsupportedOperationException("Not supported yet."); 
    } 
} 
+0

vì vậy C phải ovverride một phương pháp được sử dụng cho bằng. –

1

Vâng, nếu bạn muốn có một thời gian chạy kiểm tra xem bạn có thể làm điều gì đó như:

interface Foo{ 

} 
class A implements Foo { 

} 
class B implements Foo { 
    @Override 
    public boolean equals(Object obj) { 
     return super.equals(obj); 
    } 
} 

public static void main(String[] args) { 
    Class<A> clazzA = A.class; 
    Class<B> clazzB = B.class; 

    Class<Object> objectClass = Object.class; 
    try { 
     Method methodFromObject = objectClass.getMethod("equals",Object.class); 
     Method methodFromA = clazzA.getMethod("equals",Object.class); 
     Method methodFromB = clazzB.getMethod("equals",Object.class); 
     System.out.println("Object == A" + methodFromObject.equals(methodFromA)); 
     System.out.println("Object == B" + methodFromObject.equals(methodFromB)); 
    } catch (SecurityException e) { 
     e.printStackTrace(); 
    } catch (NoSuchMethodException e) { 
     e.printStackTrace(); 
    } 
} 

sẽ in đúng đối với người đầu tiên và sai cho phần thứ hai. Nếu bạn muốn thời gian biên dịch trông giống như tùy chọn duy nhất là tạo chú thích và sử dụng công cụ xử lý chú thích để kiểm tra xem tất cả các lớp chú thích có ghi đè bằng không.

0

Bạn có thể tự cung cấp java.util.comparator của riêng mình cho bản đồ được đề cập không?

4

Lớp trừu tượng sẽ không hoạt động nếu bạn có một cháu kể từ khi cha của nó đã ghi đè cả hai phương thức equals và hashCode và sau đó bạn có vấn đề của bạn trên một lần nữa.

Hãy thử sử dụng chú thích và APT (http://docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html) để hoàn thành công việc.