Thế hệ Java - tại sao những người khác lại mở rộng cho phép mà không phải là người thực hiện mà thôi


306

Tôi tự hỏi liệu có một lý do đặc biệt nào trong Java để luôn luôn sử dụng " extends" thay vì " implements" để xác định giới hạn của kiểu chữ.

Thí dụ:

public interface C {}
public class A<B implements C>{} 

bị cấm nhưng

public class A<B extends C>{} 

đúng. lý do cho điều đó là gì?


14
Tôi không biết tại sao mọi người nghĩ rằng câu trả lời của Tetsujin no Oni thực sự trả lời câu hỏi. Về cơ bản, nó định nghĩa lại các quan sát của OP bằng cách sử dụng từ ngữ học thuật, nhưng không đưa ra bất kỳ lý do nào. "Tại sao không có implements?" - "Bởi vì chỉ có extends".
ThomasR

1
ThomasR: đó không phải là câu hỏi "được phép", mà là về ý nghĩa: không có sự khác biệt trong cách bạn viết một loại chung tiêu thụ một loại với một ràng buộc cho dù ràng buộc là từ giao diện hay loại tổ tiên.
Tetsujin no Oni

Đã thêm một câu trả lời ( stackoverflow.com/a/56304595/4922375 ) với lý do của tôi tại sao implementssẽ không mang lại điều gì mới và sẽ làm phức tạp mọi thứ hơn nữa. Tôi hy vọng nó sẽ hữu ích cho bạn.
Andrew Tobilko

Câu trả lời:


328

Không có sự khác biệt về ngữ nghĩa trong ngôn ngữ ràng buộc chung giữa việc một lớp 'thực hiện' hay 'mở rộng'. Các khả năng ràng buộc là 'mở rộng' và 'siêu' - nghĩa là, lớp này hoạt động với khả năng được gán cho lớp kia (mở rộng) hoặc lớp này có thể được gán từ lớp đó (siêu).


@KomodoDave (Tôi nghĩ rằng số bên cạnh câu trả lời đánh dấu là đúng, tôi không chắc có cách nào khác để đánh dấu không, đôi khi các câu trả lời khác có thể chứa thông tin bổ sung - ví dụ: tôi có một vấn đề cụ thể mà tôi không thể giải quyết và google gửi cho bạn ở đây khi tìm kiếm nó.) @Tetsujin no Oni (Có thể sử dụng một số mã để làm rõ không? thanx :))
ntg

@ntg, đây là một ví dụ rất hay về một câu hỏi đang tìm ví dụ - Tôi sẽ liên kết nó trong bình luận, thay vì nhúng vào câu trả lời vào thời điểm này. stackoverflow.com/a/6828257/93922
Tetsujin no Oni

1
Tôi nghĩ lý do là ít nhất tôi muốn có một cái chung có thể có một hàm tạo và các phương thức có thể chấp nhận bất kỳ lớp nào vừa dẫn đến một lớp cơ sở vừa thể hiện một giao diện không chỉ là các giao diện mở rộng giao diện. Sau đó, có thể khởi tạo thử nghiệm Genric cho sự hiện diện của các giao diện VÀ có lớp thực tế được chỉ định làm tham số loại. Lý tưởng nhất là tôi muốn class Generic<RenderableT extends Renderable implements Draggable, Droppable, ...> { Generic(RenderableT toDrag) { x = (Draggable)toDrag; } }Một người muốn kiểm tra thời gian biên dịch.
peterk

1
@peterk Và bạn nhận được điều đó với RenderableT mở rộng Renderable, Draggable, Dropppable .... trừ khi tôi không hiểu những gì bạn muốn các tướng xóa sẽ làm cho bạn mà điều này không cung cấp.
Tetsujin no Oni

@TetsujinnoOni bạn không tham gia vào những gì tôi muốn là để thực thi thời gian biên dịch chỉ chấp nhận các lớp thực hiện một bộ giao diện nhất định, và sau đó có thể tham chiếu lớp OBOG (thể hiện các giao diện đó) trong chung và sau đó biết rằng ít nhất là tại thời gian biên dịch (và mong muốn trong thời gian chạy), mọi thứ được gán cho chung có thể được truyền một cách an toàn cho bất kỳ giao diện đã chỉ định nào. Đây không phải là trường hợp cách java được thực hiện bây giờ. Nhưng nó sẽ rất tuyệt :)
peterk

77

Câu trả lời là ở đây  :

Để khai báo một tham số loại giới hạn, hãy liệt kê tên của tham số loại, theo sau là extendstừ khóa, theo sau là [ ràng buộc ]. Lưu ý rằng, trong ngữ cảnh này, phần mở rộng được sử dụng theo nghĩa chung có nghĩa là extends(như trong các lớp) hoặc implements(như trong các giao diện).

Vì vậy, bạn có nó, nó hơi khó hiểu và Oracle biết điều đó.


1
Để thêm vào sự nhầm lẫn getFoo(List<? super Foo> fooList) CHỈ làm việc với lớp mà theo nghĩa đen được Foo mở rộng class Foo extends WildcardClass. Trong trường hợp này, List<WildcardClass>đầu vào sẽ được chấp nhận. Tuy nhiên, bất kỳ lớp nào Foothực hiện sẽ không hoạt động class Foo implements NonWorkingWildcardClasskhông có nghĩa là List<NonWorkingWildcardClass>sẽ hợp lệ trong getFoo(List<? super Foo> fooList). Tinh thể rõ ràng!
Ray

19

Có lẽ bởi vì đối với cả hai bên (B và C) chỉ có loại phù hợp, không phải việc thực hiện. Trong ví dụ của bạn

public class A<B extends C>{}

B có thể là một giao diện là tốt. "Extends" được sử dụng để định nghĩa các giao diện phụ cũng như các lớp con.

interface IntfSub extends IntfSuper {}
class ClzSub extends ClzSuper {}

Tôi thường nghĩ về 'Sub mở rộng Super' là ' Sub giống như Super , nhưng với các khả năng bổ sung' và 'Clz thực hiện Intf' vì ' Clz là sự hiện thực hóa của Intf '. Trong ví dụ của bạn, điều này sẽ phù hợp: B giống như C , nhưng với các khả năng bổ sung. Các khả năng có liên quan ở đây, không phải là nhận thức.


10
Xem xét <B kéo dài D & E>. E <caps> không được </ caps> là một lớp.
Tom Hawtin - tackline

7

Có thể loại cơ sở là một tham số chung, vì vậy loại thực tế có thể là giao diện của một lớp. Xem xét:

class MyGen<T, U extends T> {

Ngoài ra từ các giao diện phối cảnh mã máy khách gần như không thể phân biệt được với các lớp, trong khi đối với kiểu con, nó rất quan trọng.


7

Dưới đây là một ví dụ liên quan nhiều hơn về nơi cho phép mở rộng và có thể là những gì bạn muốn:

public class A<T1 extends Comparable<T1>>


5

Đó là loại tùy ý sử dụng thuật ngữ nào. Nó có thể là một trong hai cách. Có lẽ các nhà thiết kế ngôn ngữ đã nghĩ "mở rộng" là thuật ngữ cơ bản nhất và "thực hiện" là trường hợp đặc biệt cho các giao diện.

Nhưng tôi nghĩ implementssẽ có ý nghĩa hơn một chút. Tôi nghĩ rằng truyền đạt nhiều hơn rằng các loại tham số không phải có trong mối quan hệ thừa kế, chúng có thể ở bất kỳ loại mối quan hệ phụ nào.

Thuật ngữ Java thể hiện một quan điểm tương tự .


3

Chúng tôi đã từng

class ClassTypeA implements InterfaceTypeA {}
class ClassTypeB extends ClassTypeA {}

và bất kỳ sai lệch nhỏ từ các quy tắc này làm chúng ta bối rối rất nhiều.

Cú pháp của một loại ràng buộc được định nghĩa là

TypeBound:
    extends TypeVariable 
    extends ClassOrInterfaceType {AdditionalBound}

( JLS 12> 4.4. Biến loại>TypeBound )

Nếu chúng tôi thay đổi nó, chúng tôi chắc chắn sẽ thêm implementstrường hợp

TypeBound:
    extends TypeVariable 
    extends ClassType {AdditionalBound}
    implements InterfaceType {AdditionalBound}

và kết thúc với hai mệnh đề được xử lý giống hệt nhau

ClassOrInterfaceType:
    ClassType 
    InterfaceType

( JLS 12> 4.3. Các loại và giá trị tham chiếu>ClassOrInterfaceType )

ngoại trừ chúng ta cũng cần phải chăm sóc implements, điều này sẽ làm phức tạp mọi thứ hơn nữa.

Tôi tin rằng đó là lý do chính tại sao extends ClassOrInterfaceTypeđược sử dụng thay vì extends ClassTypeimplements InterfaceType- để giữ mọi thứ đơn giản trong khái niệm phức tạp. Vấn đề là chúng ta không có những từ thích hợp để trang trải tất cả extendsimplementsvà chúng tôi chắc chắn không muốn để giới thiệu một.

<T is ClassTypeA>
<T is InterfaceTypeA>

Mặc dù extendsmang lại một số mớ hỗn độn khi đi cùng với một giao diện, đó là một thuật ngữ rộng hơn và nó có thể được sử dụng để mô tả cả hai trường hợp. Cố gắng điều chỉnh tâm trí của bạn với khái niệm mở rộng một loại (không mở rộng một lớp , không thực hiện giao diện ). Bạn hạn chế tham số loại theo loại khácloại đó thực sự không quan trọng. Nó chỉ quan trọng rằng nó là giới hạn trên của nó và nó là siêu kiểu của nó .


-1

Trong thực tế, khi sử dụng chung trên giao diện, từ khóa cũng được mở rộng . Dưới đây là ví dụ mã:

Có 2 lớp thực hiện giao diện Lời chào:

interface Greeting {
    void sayHello();
}

class Dog implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("Greeting from Dog: Hello ");
    }
}

class Cat implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("Greeting from Cat: Hello ");
    }
}

Và mã kiểm tra:

@Test
public void testGeneric() {
    Collection<? extends Greeting> animals;

    List<Dog> dogs = Arrays.asList(new Dog(), new Dog(), new Dog());
    List<Cat> cats = Arrays.asList(new Cat(), new Cat(), new Cat());

    animals = dogs;
    for(Greeting g: animals) g.sayHello();

    animals = cats;
    for(Greeting g: animals) g.sayHello();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.