Java jenerikleri - “T'yi genişletir” neden “T'yi uygular” a izin verilmiyor?


306

Java'da daktilometrelerin sınırlarını tanımlamak için her zaman " extends" yerine " " kullanmak için özel bir neden olup olmadığını merak ediyorum implements.

Misal:

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

yasak ama

public class A<B extends C>{} 

doğru. Bunun nedeni nedir?


14
İnsanların neden Tetsujin no Oni'nin cevabını gerçekten soruyu cevapladığını düşündüklerini bilmiyorum. Temel olarak OP'nin akademik ifadeler kullanarak gözlemlerini yeniden ifade eder, ancak herhangi bir akıl yürütmez. "Neden yok implements?" - "Çünkü sadece var extends".
ThomasR

1
ThomasR: Bunun nedeni "izin verilen" değil, anlamın bir sorusudur: kısıtlamanın bir arabirimden mi yoksa bir atadan mı kaynaklandığına dair bir kısıtlama içeren genel bir türü nasıl yazacağınız konusunda hiçbir fark yoktur.
Tetsujin no Oni

Neden yeni bir şey getirmeyecek ve işleri daha da karmaşıklaştıracak düşüncemle bir cevap ekledim ( stackoverflow.com/a/56304595/4922375 ) implements. Umarım sizin için yararlı olacaktır.
Andrew Tobilko

Yanıtlar:


328

Bir sınıfın 'uygulama' veya 'genişletme' arasında genel kısıtlama dilinde anlamsal bir fark yoktur. Kısıtlama olasılıkları 'genişletir' ve 'süper' dir - yani, bu sınıf diğerine atanabilir (genişletir), yani bu sınıf ondan (süper) atanabilir.


@KomodoDave (Bence cevabın yanındaki sayı doğru olarak işaretler, işaretlemek için başka bir yol olup olmadığından emin değilim, bazen diğer cevaplar ek bilgi içerebilir - örneğin çözemediğim belirli bir sorunum vardı ve google sizi ararken buraya gönderir.) @Tetsujin no Oni (Açıklığa kavuşturmak için bazı kodlar kullanmak mümkün mü? thanx :))
ntg

@ntg, bu örnek arayan bir soru için çok iyi bir örnek - cevabı bu noktada yerleştirmek yerine yorumda bağlayacağım. stackoverflow.com/a/6828257/93922
Tetsujin no Oni

1
Bence neden en azından bir yapıcı ve hem bir temel sınıf axtends ve sadece bir arabirimi genişleten arabirimler bir arabirim sergileyen herhangi bir sınıfı kabul edebilecek yöntemleri var genel bir istiyorum istiyorum. Daha sonra arayüzlerin varlığı için Genric testinin başlatılmasını ve gerçek sınıfın type parametresi olarak belirtilmesini sağlayın. İdeal olarak Ben istiyorum class Generic<RenderableT extends Renderable implements Draggable, Droppable, ...> { Generic(RenderableT toDrag) { x = (Draggable)toDrag; } }derleme zaman kontrolleri istiyor.
peterk

1
@peterk Ve RenderableT ile bunu alırsınız Renderable, Draggable, Droppable .... Silmeyi sağlayan jeneriklerin sizin için ne yapmasını istemediğinizi anlamadım.
Tetsujin no Oni

@TetsujinnoOni, istediğim şeyde değil, yalnızca belirli bir arabirim kümesini uygulayan sınıfları kabul etme derlemesinin uygulanması ve sonra genel olarak OBJECT sınıfına (bu arabirimleri gösteren) başvurabilmeniz ve en azından derleme zamanında (ve istenirse çalışma zamanında) jeneriklere atanan herhangi bir şey, belirtilen arayüzlerin herhangi birine güvenle dökülebilir. Java'nın şu anda uygulanma şekli bu değildir. Ama güzel olurdu :)
peterk

77

Cevap burada  :

Sınırlı tür parametresi bildirmek için, tür parametresinin adını, ardından extendsanahtar sözcüğünü ve ardından üst sınırını […] listeleyin . Bu bağlamda, genişletmelerin genel anlamda extends(sınıflarda olduğu gibi) veya implements(arayüzlerde olduğu gibi ) anlamında kullanıldığını unutmayın .

İşte burada, biraz kafa karıştırıcı ve Oracle biliyor.


1
Karışıklık eklemek getFoo(List<? super Foo> fooList) SADECE Foo gibi genişletilmiş sınıf ile çalışır class Foo extends WildcardClass. Bu durumda a List<WildcardClass>kabul edilebilir bir girdi olur. Ancak herhangi bir sınıf Foouygular çalışma olmaz class Foo implements NonWorkingWildcardClassanlamına gelmez List<NonWorkingWildcardClass>de geçerli olacaktır getFoo(List<? super Foo> fooList). Kristal berraklığı!
Ray

19

Muhtemelen her iki taraf için de (B ve C) sadece tür ilgili, uygulama için geçerli değildir. Örneğinizde

public class A<B extends C>{}

B de bir arayüz olabilir. "extends" alt arabirimlerin yanı sıra alt sınıfları tanımlamak için kullanılır.

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

Ben genellikle 'olarak 'Alt Süper uzanır' düşünmek Alt gibidir Super ancak ek yeteneklere sahip' ve 'üç döngü halinde uygular intf' 'olarak üç döngü halinde bir hayata geçirilmesidir intf '. Örneğin, bu eşleşecektir: B , C gibidir , ancak ek yeteneklere sahiptir. Yetenekler burada gerçekleşiyor, gerçekleşme ile değil.


10
<B, D & E> 'yı genişletir. E <caps> bir sınıf olmamalı </caps>.
Tom Hawtin - tackline

7

Temel tür genel bir parametre olabilir, bu nedenle gerçek tür bir sınıfın arabirimi olabilir. Düşünmek:

class MyGen<T, U extends T> {

Ayrıca istemci kodu perspektifinden, arayüzler sınıflardan neredeyse ayırt edilemezken, alt tip için önemlidir.


7

Aşağıda, genişletmelerin nereye izin verildiğine ve muhtemelen ne istediğinize ilişkin daha kapsamlı bir örnek verilmiştir:

public class A<T1 extends Comparable<T1>>


5

Hangi terimlerin kullanılacağını keyfi bir şekilde. Her iki şekilde de olabilirdi. Belki de dil tasarımcıları en temel terim olarak “uzanır” ve arayüzler için özel bir durum olarak “uygular” diye düşünürler.

Ama bence implementsbiraz daha mantıklı. Parametre türlerinin kalıtım ilişkisinde olması gerekmediğini daha çok ilettiğini, herhangi bir alt tip ilişkide olabileceğini düşünüyorum .

Java Sözlüğü de benzer bir görüş ifade eder .


3

Biz alışkınız

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

ve bu kurallardan ufak bir sapma bizi çok şaşırtıyor.

Bağlanan türün sözdizimi şu şekilde tanımlanır:

TypeBound:
    extends TypeVariable 
    extends ClassOrInterfaceType {AdditionalBound}

( JLS 12> 4.4. Tür Değişkenleri>TypeBound )

Değiştirecek olsaydık, kesinlikle implementsdavayı eklerdik

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

ve aynı şekilde işlenmiş iki cümle ile sonuçlanır

ClassOrInterfaceType:
    ClassType 
    InterfaceType

( JLS 12> 4.3. Referans Tipleri ve Değerleri>ClassOrInterfaceType )

tek fark etmemiz gerekecek implements, ki bu da işleri daha da zorlaştıracak.

İnanıyorum ki bunun extends ClassOrInterfaceTypeyerine kullanılmasının ana nedeni extends ClassTypeve implements InterfaceType- karmaşık konsept içinde işleri basit tutmak. Sorun biz de kapsayacak şekilde doğru kelime yok olduğunu extendsve implementsve kesinlikle bir tane tanıtmak istemiyorum.

<T is ClassTypeA>
<T is InterfaceTypeA>

Her ne kadar extendsbir arayüz ile birlikte gittiğinde bazı karışıklık getirir, daha geniş bir terimdir ve her iki vakaları tanımlamak için kullanılabilir. Zihninizi bir tür genişletme kavramına ayarlamaya çalışın ( bir sınıfı genişletmemek , bir arabirim uygulamak değil ). Bir tür parametresini başka bir türle kısıtlarsınız ve bu türün gerçekte ne olduğu önemli değildir. Sadece onun üst sınırı ve onun süper tipi olduğu önemlidir .


-1

Aslında, arabirimde jenerik kullanırken, anahtar kelime de genişletilir . Kod örneği:

Selamlama arabirimini uygulayan 2 sınıf vardır:

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 ");
    }
}

Ve test kodu:

@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();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.