development

자바 다중 상속

big-blog 2020. 5. 29. 22:07
반응형

자바 다중 상속


Java의 다중 상속 문제를 해결하는 방법을 완전히 이해하기 위해 명확히해야 할 고전적인 질문이 있습니다.

I 클래스가 있다고 가정하자 Animal이 하위 클래스가 Bird그리고 Horse내가 클래스 만들 필요가 Pegasus에서 확장 Bird하고 Horse있기 때문에이 Pegasus새와 말 모두이다.

이것이 고전적인 다이아몬드 문제라고 생각합니다. 내가 이것을 해결하는 고전적인 방법을 이해할 수있는 것에서 Animal, BirdHorse클래스를 인터페이스로 만들고 구현 Pegasus하는 것입니다.

새와 말의 개체를 만들 수있는 문제를 해결하는 다른 방법이 있는지 궁금합니다. 동물을 만들 수있는 방법이 있다면 훌륭하지만 꼭 필요한 것은 아닙니다.


public interface Equidae말이나 public interface Avialae같은 동물 클래스 (생물학적 의미의 클래스)에 대한 인터페이스를 만들 수 있습니다 (생물학자가 아니므로 용어가 틀릴 수 있습니다).

그런 다음 여전히

public class Bird implements Avialae {
}

public class Horse implements Equidae {}

그리고 또한

public class Pegasus implements Avialae, Equidae {}

의견에서 추가 :

중복 코드를 줄이기 위해 구현하려는 동물의 공통 코드가 대부분 포함 된 추상 클래스를 만들 수 있습니다.

public abstract class AbstractHorse implements Equidae {}

public class Horse extends AbstractHorse {}

public class Pegasus extends AbstractHorse implements Avialae {}

최신 정보

하나 더 자세하게 추가하고 싶습니다. Brian말했듯 이 이것은 OP가 이미 알고있는 것입니다.

그러나 인터페이스에서 "다중 상속"문제를 무시하고 이미 구체적인 유형 (예 : Bird)을 나타내는 인터페이스를 사용하지 않는 것이 좋습니다 (기타 참조). 오리 타이핑도 좋습니다.하지만 생물학적 의미의 조류 인 Avialae). 또한 대문자 'I'로 시작하는 인터페이스 이름 (예 : IBird인터페이스가 필요한 이유에 대해서는 아무 것도 알려주지 않음) 을 사용하지 않는 것이 좋습니다 . 그것은 질문의 차이점입니다. 인터페이스를 사용하여 상속 계층 구조를 구성하고, 유용한 경우 추상 클래스를 사용하고, 필요한 경우 구체적인 클래스를 구현하고 필요한 경우 위임을 사용하십시오.


객체를 결합하는 데는 두 가지 기본 접근 방식이 있습니다.

  • 첫 번째는 상속 입니다. 상속의 한계를 이미 식별 했으므로 여기서 필요한 것을 수행 할 수 없음을 의미합니다.
  • 두 번째는 Composition 입니다. 상속이 실패 했으므로 구성을 사용해야합니다.

이것이 작동하는 방식은 Animal 객체가 있다는 것입니다. 그런 다음 해당 개체 내에 필요한 속성과 동작을 제공하는 추가 개체를 추가합니다.

예를 들면 다음과 같습니다.

  • 조류는 동물 구현 IFlier 연장
  • 확장 동물 구현 IHerbivore, IQuadruped
  • 페가수스 확장 동물 구현을 IHerbivore, IQuadruped, IFlier

이제 IFlier다음과 같이 보입니다.

 interface IFlier {
     Flier getFlier();
 }

따라서 Bird다음과 같습니다

 class Bird extends Animal implements IFlier {
      Flier flier = new Flier();
      public Flier getFlier() { return flier; }
 }

이제 당신은 상속의 모든 장점을 가지고 있습니다. 코드를 재사용 할 수 있습니다. IFliers 컬렉션을 가질 수 있고 다형성 등의 다른 모든 장점을 사용할 수 있습니다.

그러나 컴포지션의 모든 유연성도 있습니다. Animal각 비트의 설정 방법에 대해 필요한만큼의 제어를 통해 각 유형에 원하는만큼 다양한 인터페이스와 복합 백킹 클래스를 적용 할 수 있습니다 .

구성에 대한 전략 패턴 대안 적 접근

수행하는 작업과 방법에 따른 다른 방법은 Animal기본 클래스에 여러 가지 동작 목록을 유지하기 위해 내부 컬렉션을 포함시키는 것입니다. 이 경우 전략 패턴에 더 가까운 것을 사용하게됩니다. 그것은 코드 단순화 측면에서 이점을 제공하지만 (예 : 또는 Horse에 대해 아무것도 알 필요가 없습니다 ) 인터페이스 접근 방식을 수행하지 않으면 다형성 등의 많은 이점을 잃게됩니다.QuadrupedHerbivore


나는 바보 같은 생각을 가지고있다 :

public class Pegasus {
    private Horse horseFeatures; 
    private Bird birdFeatures; 

   public Pegasus(Horse horse, Bird bird) {
     this.horseFeatures = horse;
     this.birdFeatures = bird;
   }

  public void jump() {
    horseFeatures.jump();
  }

  public void fly() {
    birdFeatures.fly();
  }
}

오리 타이핑 의 개념을 제안해도 될까요?

페가수스가 새와 말 인터페이스를 확장시키는 경향이 있지만 오리 타이핑은 실제로 행동을 상속해야한다는 것을 암시합니다 . 의견에서 이미 언급했듯이 페가수스는 새가 아니라 날 수 있습니다. 따라서 페가수스는 오히려- Flyable인터페이스를 상속 받아- 인터페이스라고 말해야합니다 Gallopable.

이런 종류의 개념은 전략 패턴 에서 활용됩니다 . 주어진 예는 실제로 오리 상속이 방법을 보여줍니다 FlyBehaviour그리고 QuackBehaviour아직도, 오리가있을 수 있습니다 예를 들어 RubberDuck, 비행 할 수있다. 그들은 또한 Duck확장을 한 Bird클래스 로 만들 수 있었지만 모든 Duck사람들은 심지어 가난한 사람들조차도 날 수 있기 때문에 약간의 유연성을 포기했을 RubberDuck입니다.


기술적으로 말하면 한 번에 하나의 클래스 만 확장하고 여러 인터페이스를 구현할 수 있지만 소프트웨어 엔지니어링을 할 때는 일반적으로 대답 할 수없는 문제 별 솔루션을 제안합니다. 그건 그렇고, OO 관행 은 콘크리트 클래스를 확장 하지 않고 원치 않는 상속 동작을 방지하기 위해 추상 클래스 만 확장하는 것입니다. "동물"과 같은 동물 개체는 없으며 콘크리트 동물 만 사용하는 것은 없습니다.


2014 년 2 월 현재 개발 단계에있는 Java 8에서는 기본 메소드사용 하여 일종의 C ++와 유사한 다중 상속을 달성 할 수 있습니다 . 공식 문서보다 작업을 시작하기에 더 쉬운 몇 가지 예를 보여주는 이 자습서살펴볼 수도 있습니다 .


말이 반 문을 넘을 수 없으므로 말을 반 문으로 안정되게 유지하는 것이 안전합니다. 따라서 나는 말 유형의 모든 품목을 받아들이고 반문으로 안정에 넣는 말 주택 서비스를 설정했습니다.

말처럼 날 수있는 동물 같은 말입니까?

다중 상속에 대해 많이 생각했지만 15 년 이상 프로그래밍을 했으므로 더 이상 다중 상속 구현에 신경 쓰지 않습니다.

다중 상속을 향한 디자인에 대처하려고 할 때 종종 문제 영역을 이해하지 못했음을 발표했습니다.

또는

오리처럼 보이고 오리처럼 찌르지 만 배터리가 필요한 경우 추상화가 잘못되었을 수 있습니다 .


Java에는 다중 상속이 없으므로 다중 상속 문제가 없습니다. 이것은 실제 다중 상속 문제 (다이아몬드 문제)를 해결하기 위해 의도적으로 설계된 것입니다.

문제를 완화하기위한 다양한 전략이 있습니다. 가장 즉각적으로 달성 할 수있는 것은 Pavel이 제안하는 Composite 객체입니다 (본질적으로 C ++에서이를 처리하는 방법). C3 선형화 (또는 이와 유사한)를 통한 다중 상속이 Java의 미래 카드에 있는지는 모르겠지만 의심합니다.

당신의 질문이 학문적이라면, 올바른 해결책은 Bird and Horse가 더 구체적이며, Pegasus가 단순히 Bird and Horse가 결합 된 것이라고 가정하는 것은 잘못된 것입니다. 페가수스는 조류와 말과 공통적 인 고유 속성을 가지고 있다고 말하는 것이 더 정확할 것입니다 (즉, 아마도 공통 조상을 가지고 있음). 이것은 Moritz의 답변이 지적한대로 충분히 모델링 될 수 있습니다.


나는 그것이 당신의 필요와 동물 클래스가 코드에서 어떻게 사용되는지에 달려 있다고 생각합니다.

Pegasus 클래스 내에서 Horse and Bird 구현의 메소드와 기능을 사용하려면 Pegasus를 Bird와 Horse 구성 으로 구현할 수 있습니다 .

public class Animals {

    public interface Animal{
        public int getNumberOfLegs();
        public boolean canFly();
        public boolean canBeRidden();
    }

    public interface Bird extends Animal{
        public void doSomeBirdThing();
    }
    public interface Horse extends Animal{
        public void doSomeHorseThing();
    }
    public interface Pegasus extends Bird,Horse{

    }

    public abstract class AnimalImpl implements Animal{
        private final int numberOfLegs;

        public AnimalImpl(int numberOfLegs) {
            super();
            this.numberOfLegs = numberOfLegs;
        }

        @Override
        public int getNumberOfLegs() {
            return numberOfLegs;
        }
    }

    public class BirdImpl extends AnimalImpl implements Bird{

        public BirdImpl() {
            super(2);
        }

        @Override
        public boolean canFly() {
            return true;
        }

        @Override
        public boolean canBeRidden() {
            return false;
        }

        @Override
        public void doSomeBirdThing() {
            System.out.println("doing some bird thing...");
        }

    }

    public class HorseImpl extends AnimalImpl implements Horse{

        public HorseImpl() {
            super(4);
        }

        @Override
        public boolean canFly() {
            return false;
        }

        @Override
        public boolean canBeRidden() {
            return true;
        }

        @Override
        public void doSomeHorseThing() {
            System.out.println("doing some horse thing...");
        }

    }

    public class PegasusImpl implements Pegasus{

        private final Horse horse = new HorseImpl();
        private final Bird bird = new BirdImpl();


        @Override
        public void doSomeBirdThing() {
            bird.doSomeBirdThing();
        }

        @Override
        public int getNumberOfLegs() {
            return horse.getNumberOfLegs();
        }

        @Override
        public void doSomeHorseThing() {
            horse.doSomeHorseThing();
        }


        @Override
        public boolean canFly() {
            return true;
        }

        @Override
        public boolean canBeRidden() {
            return true;
        }
    }
}

또 다른 가능성은 동물을 정의하기 위해 상속 대신 Entity-Component-System 접근법을 사용하는 것입니다. 물론 이것은 동물의 개별 Java 클래스를 가지지 않고 구성 요소에 의해서만 정의된다는 것을 의미합니다.

Entity-Component-System 접근 방식에 대한 일부 의사 코드는 다음과 같습니다.

public void createHorse(Entity entity){
    entity.setComponent(NUMER_OF_LEGS, 4);
    entity.setComponent(CAN_FLY, false);
    entity.setComponent(CAN_BE_RIDDEN, true);
    entity.setComponent(SOME_HORSE_FUNCTIONALITY, new HorseFunction());
}

public void createBird(Entity entity){
    entity.setComponent(NUMER_OF_LEGS, 2);
    entity.setComponent(CAN_FLY, true);
    entity.setComponent(CAN_BE_RIDDEN, false);
    entity.setComponent(SOME_BIRD_FUNCTIONALITY, new BirdFunction());
}

public void createPegasus(Entity entity){
    createHorse(entity);
    createBird(entity);
    entity.setComponent(CAN_BE_RIDDEN, true);
}

인터페이스 계층 구조를 가지고 선택한 인터페이스에서 클래스를 확장 할 수 있습니다.

public interface IAnimal {
}

public interface IBird implements IAnimal {
}

public  interface IHorse implements IAnimal {
}

public interface IPegasus implements IBird,IHorse{
}

그런 다음 특정 인터페이스를 확장하여 필요에 따라 클래스를 정의하십시오.

public class Bird implements IBird {
}

public class Horse implements IHorse{
}

public class Pegasus implements IPegasus {
}

Ehm, 당신의 클래스는 오직 하나의 서브 클래스 일 수 있지만, 원하는만큼 많은 인터페이스를 구현할 수 있습니다.

A Pegasus is in fact a horse (it is a special case of a horse), which is able to fly (which is the "skill" of this special horse). From the other hand, you can say, the Pegasus is a bird, which can walk, and is 4legged - it all depends, how it is easier for you to write the code.

Like in your case you can say:

abstract class Animal {
   private Integer hp = 0; 
   public void eat() { 
      hp++; 
   }
}
interface AirCompatible { 
   public void fly(); 
}
class Bird extends Animal implements AirCompatible { 
   @Override
   public void fly() {  
       //Do something useful
   }
} 
class Horse extends Animal {
   @Override
   public void eat() { 
      hp+=2; 
   }

}
class Pegasus extends Horse implements AirCompatible {
   //now every time when your Pegasus eats, will receive +2 hp  
   @Override
   public void fly() {  
       //Do something useful
   }
}

Interfaces don't simulate multiple inheritance. Java creators considered multiple inheritance wrong, so there is no such thing in Java.

If you want to combine the functionality of two classes into one - use object composition. I.e.

public class Main {
    private Component1 component1 = new Component1();    
    private Component2 component2 = new Component2();
}

And if you want to expose certain methods, define them and let them delegate the call to the corresponding controller.

Here interfaces may come handy - if Component1 implements interface Interface1 and Component2 implements Interface2, you can define

class Main implements Interface1, Interface2

So that you can use objects interchangeably where the context allows it.

So in my point of view, you can't get into diamond problem.


As you will already be aware, multiple inheritance of classes in Java is not possible, but it's possible with interfaces. You may also want to consider using the composition design pattern.

I wrote a very comprehensive article on composition a few years ago...

https://codereview.stackexchange.com/questions/14542/multiple-inheritance-and-composition-with-java-and-c-updated


  1. Define interfaces for defining the capabilities. You can define multiple interfaces for multiple capabilities. These capabilities can be implemented by specific Animal or Bird.
  2. Use inheritance to establish relationships among classes by sharing non-static and non-public data/methods.
  3. Use Decorator_pattern to add capabilities dynamically. This will allow you to reduce number of inheritance classes & combinations.

Have a look at below example for better understanding

When to Use the Decorator Pattern?


To reduce the complexity and simplify the language, multiple inheritance is not supported in java.

Consider a scenario where A, B and C are three classes. The C class inherits A and B classes. If A and B classes have same method and you call it from child class object, there will be ambiguity to call method of A or B class.

Since compile time errors are better than runtime errors, java renders compile time error if you inherit 2 classes. So whether you have same method or different, there will be compile time error now.

class A {  
    void msg() {
        System.out.println("From A");
    }  
}

class B {  
    void msg() {
        System.out.println("From B");
    }  
}

class C extends A,B { // suppose if this was possible
    public static void main(String[] args) {  
        C obj = new C();  
        obj.msg(); // which msg() method would be invoked?  
    }
} 

To solve the problem of mutiple inheritance in Java → interface is used

J2EE (core JAVA) Notes By Mr. K.V.R Page 51

Day - 27

  1. Interfaces are basically used to develop user defined data types.
  2. With respect to interfaces we can achieve the concept of multiple inheritances.
  3. With interfaces we can achieve the concept of polymorphism, dynamic binding and hence we can improve the performance of a JAVA program in turns of memory space and execution time.

An interface is a construct which contains the collection of purely undefined methods or an interface is a collection of purely abstract methods.

[...]

Day - 28:

Syntax-1 for reusing the features of interface(s) to class:

[abstract] class <clsname> implements <intf 1>,<intf 2>.........<intf n>
{
    variable declaration;
    method definition or declaration;
};

In the above syntax clsname represents name of the class which is inheriting the features from ‘n’ number of interfaces. ‘Implements’ is a keyword which is used to inherit the features of interface(s) to a derived class.

[...]

Syntax-2 inheriting ‘n’ number of interfaces to another interface:

interface <intf 0 name> extends <intf 1>,<intf 2>.........<intf n>
{     
    variable declaration cum initialization;
    method declaration;
};

[...]

Syntax-3:

[abstract] class <derived class name> extends <base class name> implements <intf 1>,<intf 2>.........<intf n>
{
  variable declaration;
  method definition or declaration;
};

참고URL : https://stackoverflow.com/questions/21824402/java-multiple-inheritance

반응형