2012-05-04 44 views
5

Điều này có vẻ là một câu hỏi java cơ bản.Giới thiệu về tiểu loại phụ Java

Tôi có một giao diện, Pipeline, có phương thức execute(Stage).

Sau đó, tôi tạo giao diện phụ để mở rộng từ Pipeline, giả sử BookPipeline, tôi thích phương thức là execute(BookStage).

BookStage kéo dài từ Stage.

Dường như loại định nghĩa này không thể chuyển biên dịch java.

Bất kỳ đề xuất nào về điều đó?

+1

được thông báo lỗi biên dịch là gì? Mã của bạn là gì? –

Trả lời

6

Bạn có thể muốn xem xét sử dụng Generics.

public interface Pipeline<T extends Stage> { 
    public void execute(T stage); 
} 

public interface BookPipeline extends Pipeline<BookStage> { 
    @Override 
    public void execute(BookStage stage); 
} 
+4

Không phải là tuyên bố của 'thực thi' trong' Sách kỷ luật 'không? –

+0

@KirkWoll Có, đó là vì lợi ích của ví dụ – Jeffrey

+0

Gotcha, tôi không cần ghi đè lên nó. Cám ơn rất nhiều! – BlueDolphin

4

Ngoài những gì @Jeffrey đã viết là giải pháp khả thi, điều quan trọng là phải hiểu tại sao bạn không thể làm điều đó.

Giả sử bạn có giao diện Pipeline với phương thức execute(Stage) và giao diện mở rộng BookPipeline với execute(BookStage).

Cũng giả sử bạn có một số lớp học Conc triển khai BookPipeline.

Hãy xem xét những điều sau

Pipeline p = new Conc(); 
p.execute(new Stage()); 

Điều gì sẽ xảy ra? Nó sẽ không an toàn!
Java muốn tránh điều đó và do đó ngăn chặn tình huống này ngay từ đầu.

Quy tắc là một lớp/giao diện mở rộng có thể thêm hành vi, nhưng không làm giảm hoạt động.

+0

Rất tốt, cảm ơn! – BlueDolphin

0

Chỉ cần để xây dựng trên câu trả lời @amit 's, đoạn mã không an toàn như là phương pháp Conc.execute mất một BookStage như một tham số và điều này sẽ được cố gắng để ép một Stage thay cho rằng (và dĩ nhiên, không phải tất cả Stage s là BookStage s).

Tuy nhiên, hãy tưởng tượng chúng tôi muốn đi theo con đường khác, có nghĩa là, làm cho các kiểu tham số của BookePipeline.execute một loại siêu của Stage, chẳng hạn như Object.

Vì vậy, chỉ cần làm rõ, chúng ta sẽ có:

interface Pipeline 
{ 
    void execute(Stage s); 
} 

interface BookPipeline extends Pipeline 
{ 
    @Override 
    void execute(Object s); 
} 

Và nơi Conc cụ BookPipeline:

Pipeline p = new Conc(); 
p.execute(new Stage()); 

này sẽ, về mặt lý thuyết, được an toàn vì Liskov năng thay thế đã không bị vi phạm - chúng tôi có thể chuyển an toàn Stage vào bất kỳ triển khai nào có thông số Stage trở lên. Điều này được gọi là contravariance. Java không hỗ trợ các loại đối số contravariant, tuy nhiên có languages.

Câu hỏi ban đầu của bạn liên quan đến covariant loại đối số không an toàn vì lý do được chỉ định (tuy nhiên, đủ lạ, một ngôn ngữ được gọi là Eiffel cho phép điều này).

Tuy nhiên, Java hỗ trợ biến thể trả lại loại. Hãy tưởng tượng Pipeline đã có một

Stage getAStage(); 

nó sẽ là hoàn toàn hợp pháp cho BookPipeline để ghi đè phương pháp này như sau:

@Override 
BookStage getAStage(); 

Sau đó tưởng tượng chúng ta có:

public void someMethodSomewhere(Pipeline p) 
{ 
    Stage s = p.getAStage(); 
    //do some dance on Stage 
} 

Giả sử chúng ta có một số lớp Donc đã triển khai Pipeline và vượt quá getAStage() chính xác như được xác định trong Pipeline (do đó vẫn trở Stage), cả hai cuộc gọi là OK:

someMethodSomewhere(new Conc()); 
someMethodSomewhere(new Donc()); 

Bởi vì chúng ta luôn có thể đặt một bất cứ điều gì Stage hoặc ít hơn (ví dụ BookStage) trong một biến loại Stage.

Vì vậy, để xây dựng lại và các quy tắc liên hệ đặc biệt với phương pháp trọng, một lớp mở rộng/giao diện đó sẽ ghi đè các phương pháp, chỉ có thể thực hiện các phương pháp đó tổng quát hơn trong những gì họ chấp nhận và cụ thể hơn trong những gì họ quay trở lại. (mặc dù trong trường hợp của Java, kiểu trả về chỉ cụ thể hơn được cho phép.)

Chỉ cần nhớ, PECS - Nhà sản xuất mở rộng, người tiêu dùng Siêu (Joshua Bloch, Effective Java)

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