2017-12-14 9 views
2

공변 반환 형식의 메서드를 재정의 할 수 있지만 기본값 메서드를 공변 반환 형식으로 재정의 할 수 있습니까? 다음 예제에서는 기본 메서드를 다시 작성하지 않고 getFirstLeg를 재정의하고 싶지만 Java에서는이를 허용하지 않습니다. 또한 많은 매개 변수가있을 수 있고 Animal이 많은 장소에서 사용되기 때문에 Animal을 일반 인터페이스로 만들고 싶지 않습니다.기본 메서드의 공변 반환 형식

interface Leg { 
} 

interface Animal { 
    List<? extends Leg> getLegs(); 

    default Leg getFirstLeg() { 
     return getLegs().get(0); 
    } 
} 

abstract class AnimalImpl<T extends Leg> implements Animal { 
    private List<T> legs; 

    @Override 
    public List<T> getLegs() { 
     return legs; 
    } 
} 

interface DuckLeg extends Leg { 
} 

interface Duck extends Animal { 
    @Override 
    List<? extends DuckLeg> getLegs(); //covariant return type 

    @Override 
    DuckLeg getFirstLeg(); //I do not want to rewrite this method 
} 

class DuckImpl extends AnimalImpl<DuckLeg> implements Duck { 
    //Error: java: DuckImpl is not abstract and does not override abstract method getFirstLeg() in Duck 
} 

업데이트 : 다음 코드는 컴파일되지만 새로운 문제는 오리가 더 이상 동물이 아닙니다.

interface Leg { 
} 

interface AnimalGeneric<T extends Leg> { 
    List<? extends T> getLegs(); 

    default T getFirstLeg() { 
     return getLegs().get(0); 
    } 
} 

abstract class AnimalImpl<T extends Leg> implements AnimalGeneric<T> { 
    private List<T> legs; 

    @Override 
    public List<T> getLegs() { 
     return legs; 
    } 
} 

interface Animal extends AnimalGeneric<Leg> { 
    //empty 
} 

interface BirdLeg extends Leg { 
} 

interface BirdGeneric<T extends BirdLeg> extends AnimalGeneric<T> { 
} 

class BirdImpl<T extends BirdLeg> extends AnimalImpl<T> implements BirdGeneric<T> { 
} 

interface Bird extends BirdGeneric<BirdLeg> { 
    //empty 
} 

interface DuckLeg extends BirdLeg { 
} 

class DuckImpl extends BirdImpl<DuckLeg> implements Duck { 
} 

interface Duck extends BirdGeneric<DuckLeg> { 
} 

답변

0

예, Java에는 상속 된 메소드에 대한 공변 (covariant) 리턴 유형이 있습니다. 그러나 여기서의 문제는 getFirstLegdefault 메쏘드라는 사실이 아니라, 자바의 generics가 공변 적이라는 것입니다. 그들은 불변합니다. 나는. A List<DuckLeg>DuckLegLeg 인 경우에도 List<? extends Leg>이 아닙니다.

그러나 Animal 인터페이스를 일반화하여이 문제를 해결할 수 있으므로 type 매개 변수는 하위 인터페이스를 기반으로 변경할 수 있습니다.

interface Animal<T extends Leg> { 
    List<T> getLegs(); 

    default T getFirstLeg() { 
     return getLegs().get(0); 
    } 
} 

Duck에는 형식 매개 변수도 있지만 더 이상 재정의 할 필요가 없습니다. 형식 인수 DuckLegT에 할당됩니다.

interface Duck extends Animal<DuckLeg> { 
    // able to remove the override of getLegs 
    //@Override 
    //List<DuckLeg> getLegs(); //covariant return type 

    // able to remove the override of getFirstLeg 
    //@Override 
    //DuckLeg getFirstLeg(); //I do not want to rewrite this method 
} 

는 그런 다음 DuckImpl 클래스는 제대로 모든 것을 상속, 당신은 "추상적 인하지를하고 추상 메소드 오버라이드 (override)하지 않는다"받지 않습니다 오류가 발생했습니다.

물론 Duck이라는 빈 선언이 있으면 전혀 필요하지 않을 수 있습니다. DuckImplAnimal<DuckLeg>을 구현하면됩니다.

class DuckImpl extends AnimalImpl<DuckLeg> implements Animal<DuckLeg> { 

} 
+0

내 게시물에 마지막 문장을 참조하십시오. Animal (또는 다른 널리 사용되는 인터페이스)을 만드는 것에 대한 우려가 있습니다. 매개 변수를 변경하려면 많은 장소를 변경해야하기 때문입니다. – Fan

+0

목록 은 목록이 아니지만 를 확장하고 List List ? – Fan

+0

자바 제네릭이 불변하기 때문에 '동물성'제네릭을 사용하는 것이 문제를 정확하게 해결하는 유일한 방법입니다. 또한'List ''List 를 확장합니다. 'List 는'List '을 나타낼 수 있고'List 는'List '을 나타낼 수 있습니다. 불일치 가능성이 있기 때문에 컴파일러는 와일드 카드로 공변량 제네릭을 허용하지 않아야합니다. – rgettman

0

공분산을 원하면 Leg을 입력하십시오. 그런 다음 대부분의 코드를 삭제할 수 있습니다.

이 컴파일 :

interface Leg { 
} 

interface Animal<T extends Leg> { 
    List<T> getLegs(); 

    default T getFirstLeg() { 
     return getLegs().get(0); 
    } 
} 

abstract class AnimalImpl<T extends Leg> implements Animal<T> { 
    private List<T> legs; 

    @Override 
    public List<T> getLegs() { 
     return legs; 
    } 
} 

interface DuckLeg extends Leg { 
} 

interface Duck extends Animal<DuckLeg> { 
} 

class DuckImpl extends AnimalImpl<DuckLeg> implements Duck { 
    // no errors 
} 
+0

내 게시물의 마지막 문장을 참조하십시오. Animal (또는 다른 널리 사용되는 인터페이스)을 만드는 것에 대한 우려가 있습니다. 매개 변수를 변경하려면 많은 장소를 변경해야하기 때문입니다. – Fan