수색…


소개

상속은 키워드 extends 사용하여 한 클래스가 다른 클래스의 속성을 획득하고 확장하는 기본 객체 지향 기능입니다. 인터페이스 및 키워드 implements 에 대해서는 인터페이스를 참조하십시오.

통사론

  • ClassB 클래스는 ClassA를 확장합니다. {...}
  • ClassB 클래스는 InterfaceA {...}를 구현합니다.
  • 인터페이스 InterfaceB는 InterfaceA {...}를 확장합니다.
  • class ClassB extends ClassA는, InterfaceC, InterfaceD {...}를 구현합니다.
  • abstract class AbstractClassB는 ClassA를 확장합니다. {...}
  • abstract class AbstractClassB는 AbstractClassA를 확장합니다. {...}
  • abstract class AbstractClassB extends ClassA는, InterfaceC, InterfaceD {...}를 구현합니다.

비고

상속은 종종 제네릭과 결합되어 기본 클래스가 하나 이상의 유형 매개 변수를 갖습니다. 일반 클래스 만들기를 참조하십시오.

초등 수업

추상 클래스는 abstract 키워드로 표시된 클래스입니다. 비 추상 클래스와 달리 추상 구현이없는 메소드가 포함될 수 있습니다. 그러나 추상적 인 방법없이 추상 클래스를 만드는 것은 유효합니다.

추상 클래스는 인스턴스화 될 수 없습니다. 하위 클래스가 추상 클래스이거나 슈퍼 클래스로 추상으로 표시된 모든 메소드를 구현하는 한 하위 클래스 (확장) 일 수 있습니다.

추상 클래스의 예 :

public abstract class Component {
    private int x, y;
    
    public setPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public abstract void render();
}

적어도 하나의 추상 메소드가 있으면 클래스는 추상으로 표시되어야합니다. 추상 메소드는 구현이없는 메소드입니다. 모든 하위 클래스에 공통 코드를 제공하기 위해 구현이있는 추상 클래스 내에서 다른 메소드를 선언 할 수 있습니다.

이 클래스를 인스턴스화하려고하면 컴파일 오류가 발생합니다.

//error: Component is abstract; cannot be instantiated   
Component myComponent = new Component();

그러나 Component 를 확장하고 모든 추상 메서드에 대한 구현을 제공하며 인스턴스화 될 수있는 클래스입니다.

public class Button extends Component {

    @Override
    public void render() {
        //render a button
    }
}

public class TextBox extends Component {

    @Override
    public void render() {
        //render a textbox
    }
}

상속 클래스의 인스턴스는 부모 클래스 (일반적인 상속)로 캐스팅 될 수 있으며 추상 메소드가 호출 될 때 다형성 효과를 제공합니다.

Component myButton = new Button();
Component myTextBox = new TextBox();

myButton.render(); //renders a button
myTextBox.render(); //renders a text box

추상 클래스 대 인터페이스

abstract 클래스와 인터페이스는 메소드 시그니처를 정의하는 방법을 제공하는 반면 구현을 제공하는 확장 / 구현 클래스가 필요합니다.

추상 클래스와 인터페이스에는 두 가지 중요한 차이점이 있습니다.

  • 클래스는 단일 클래스 만 확장 할 수 있지만 많은 인터페이스를 구현할 수 있습니다.
  • 추상 클래스에는 인스턴스 (비 static ) 필드가 포함될 수 있지만 인터페이스에는 static 필드 만 포함될 수 있습니다.
Java SE 8

인터페이스로 선언 된 메소드는 구현을 포함 할 수 없었기 때문에, 추상 메소드는 추가 메소드를 제공하는 데 유용 할 때 추상 클래스가 사용되었습니다.

Java SE 8

Java 8은 인터페이스가 인터페이스 의 다른 메소드를 사용하여 구현되는 기본 메소드 를 포함 할 수있게하여 인터페이스와 추상 클래스를 동등하게 강력하게 만듭니다.

추상 클래스의 익명 서브 클래스

편리한 java가 새 클래스를 생성 할 때 추상 메소드에 대한 구현을 제공하는 추상 클래스의 서브 클래스의 익명 인스턴스를 인스턴스화 할 수 있도록합니다. 위의 예제를 사용하면 다음과 같이 보일 수 있습니다.

Component myAnonymousComponent = new Component() {
    @Override
    public void render() {
        // render a quick 1-time use component
    }
}

정적 상속

정적 메서드는 일반적인 메서드와 비슷한 상속 될 수 있지만 일반적인 메서드와 달리 정적 메서드 재정의를 강제하려면 " 추상 "메서드를 만들 수 없습니다. 수퍼 클래스에서 정적 메서드와 같은 서명을 가진 메서드를 작성하는 것은 오버라이드의 한 형태 인 것처럼 보이지만, 실제로 이것은 단순히 새로운 함수를 생성하여 다른 개체를 숨 깁니다.

public class BaseClass {
    
    public static int num = 5;

    public static void sayHello() {
        System.out.println("Hello");
    }

    public static void main(String[] args) {
        BaseClass.sayHello();
        System.out.println("BaseClass's num: " + BaseClass.num);
            
        SubClass.sayHello();
        //This will be different than the above statement's output, since it runs
        //A different method
        SubClass.sayHello(true);
        
        StaticOverride.sayHello();
        System.out.println("StaticOverride's num: " + StaticOverride.num);
    }
}

public  class SubClass extends BaseClass {
    
    //Inherits the sayHello function, but does not override it   
    public static void sayHello(boolean test) {
        System.out.println("Hey");
    }
}

public static class StaticOverride extends BaseClass {

    //Hides the num field from BaseClass
    //You can even change the type, since this doesn't affect the signature
    public static String num = "test";
        
    //Cannot use @Override annotation, since this is static
    //This overrides the sayHello method from BaseClass
    public static void sayHello() {
        System.out.println("Static says Hi");
    }

}

이러한 클래스를 실행하면 다음과 같은 출력이 생성됩니다.

Hello
BaseClass's num: 5
Hello
Hey
Static says Hi
StaticOverride's num: test

일반적인 상속과는 달리, 정적 상속 메소드는 숨겨지지 않습니다. BaseClass.sayHello() 를 사용하여 기본 sayHello 메서드를 호출 할 수 있습니다. 그러나 같은 서명을 가진 메소드가 서브 클래스에서 발견되지 않으면 클래스는 정적 메소드를 상속받습니다. 2 개의 메소드의 시그 니챠가 다른 경우, 이름이 같더라도 두 메소드를 서브 클래스에서 실행할 수 있습니다.

정적 필드는 비슷한 방식으로 서로를 숨 깁니다.

'final'을 사용하여 상속과 오버라이드 제한하기

최종 수업

class 선언에서 사용될 때 final 한정자는 클래스를 extend 하는 다른 클래스가 선언되지 않도록합니다. final 클래스는 상속 클래스 계층의 "리프"클래스입니다.

// This declares a final class
final class MyFinalClass {
    /* some code */
}

// Compilation error: cannot inherit from final MyFinalClass
class MySubClass extends MyFinalClass {
    /* more code */
}

최종 클래스의 유스 케이스

최종 클래스는 private 생성자와 결합하여 클래스의 인스턴스화를 제어하거나 방지 할 수 있습니다. 정적 멤버만을 정의하는 소위 "유틸리티 클래스"를 생성하는 데 사용할 수 있습니다. 즉 상수와 정적 메소드.

public final class UtilityClass {

    // Private constructor to replace the default visible constructor
    private UtilityClass() {}

    // Static members can still be used as usual
    public static int doSomethingCool() {
        return 123;
    }

}

변경할 수없는 클래스도 final 로 선언해야합니다. (불변의 클래스 란, 작성 후에 인스턴스를 변경할 수 없기 때문에, 「변경 가능한 객체」의 항목을 참조 해주세요 .) 그렇게하면 (자), 변경 불가능한 클래스의 변경 가능한 서브 클래스를 작성할 수 없게됩니다. 그것은 하위 유형이 그 수퍼 타입의 "행동 계약"을 따라야한다는 Liskov Substitution Principle 을 위반할 것입니다.

실용적인 관점에서 불변 클래스를 final 선언하면 프로그램 동작에 대한 이유를 쉽게 알 수 있습니다. 또한 보안 샌드 박스에서 신뢰할 수없는 코드가 실행되는 시나리오의 보안 문제를 해결합니다. 예를 들어, Stringfinal 로 선언 되었기 때문에 신뢰할 수있는 클래스는 신뢰할 수없는 호출자가 무의식적으로 변경할 수있는 변경 가능한 하위 클래스를 받아들이는 것을 속일 수 있다고 걱정할 필요가 없습니다.

final 클래스의 한 가지 단점은 Mockito와 같은 일부 조롱 (mocking) 프레임 워크에서는 작동하지 않는다는 것입니다. 업데이트 : Mockito 버전 2는 최종 수업 조롱을 지원합니다.

최종 방법

final 수식자는, 서브 클래스로 오버라이드 (override)되는 것을 막는 메소드에도 적용 할 수 있습니다.

public class MyClassWithFinalMethod {

    public final void someMethod() {
    }
}

public class MySubClass extends MyClassWithFinalMethod {

    @Override
    public void someMethod() { // Compiler error (overridden method is final)
    }
}

최종 메소드는 일반적으로 하위 클래스가 클래스에서 변경할 수있는 것을 제한하려는 경우 일반적으로 사용됩니다.


final 수정자는 변수에도 적용 할 수 있지만 변수의 final 은 상속과 관련이 없습니다.

Liskov 대체 원리

Substitutability는 1987 년 회의 기조 연설에서 Barbara Liskov가 소개 한 객체 지향 프로그래밍의 원칙으로, 클래스 B 가 클래스 A 의 하위 클래스이고 A 가 예상되는 모든 위치에 B 가 대신 사용될 수 있다는 것을 나타냅니다.

class A {...}
class B extends A {...}

public void method(A obj) {...}

A a = new B(); // Assignment OK
method(new B()); // Passing as parameter OK

이것은 유형이 인터페이스 인 경우에도 적용됩니다. 여기서 인터페이스간에 객체 간의 계층 적 관계가 필요하지 않습니다.

interface Foo {
    void bar();
}

class A implements Foo {
    void bar() {...}
}

class B implements Foo {
    void bar() {...}
}

List<Foo> foos = new ArrayList<>();
foos.add(new A()); // OK
foos.add(new B()); // OK

이제 목록에는 동일한 클래스 계층 구조가 아닌 개체가 포함됩니다.

계승

클래스간에 extends 키워드를 사용하면 상위 클래스 ( 상위 클래스 또는 기본 클래스 라고도 함)의 모든 속성이 하위 클래스 ( 하위 클래스 또는 파생 클래스 라고도 함)에 있습니다.

public class BaseClass {

    public void baseMethod(){
        System.out.println("Doing base class stuff");
    }
}

public class SubClass extends BaseClass {

}

SubClass 인스턴스는 baseMethod() 메소드를 상속 baseMethod() .

SubClass s = new SubClass();
s.baseMethod();  //Valid, prints "Doing base class stuff"

서브 클래스에 추가 컨텐츠를 추가 할 수 있습니다. 이렇게하면 기본 클래스 나 그 같은 기본 클래스의 다른 하위 클래스를 변경하지 않고 하위 클래스의 추가 기능을 사용할 수 있습니다.

public class Subclass2 extends BaseClass {

    public void anotherMethod() {
        System.out.println("Doing subclass2 stuff");
    }
}

Subclass2 s2 = new Subclass2();
s2.baseMethod(); //Still valid , prints "Doing base class stuff"
s2.anotherMethod(); //Also valid, prints "Doing subclass2 stuff" 

필드도 상속됩니다.

public class BaseClassWithField {

    public int x;

}

public class SubClassWithField extends BaseClassWithField {

    public SubClassWithField(int x) {
        this.x = x; //Can access fields
    }
}

private 필드와 메소드는 여전히 서브 클래스 내에 존재하지만 액세스 할 수는 없습니다.

public class BaseClassWithPrivateField {

    private int x = 5;

    public int getX() {
        return x;
    }
}

public class SubClassInheritsPrivateField extends BaseClassWithPrivateField {

    public void printX() {
        System.out.println(x); //Illegal, can't access private field x
        System.out.println(getX()); //Legal, prints 5
    }
}

SubClassInheritsPrivateField s = new SubClassInheritsPrivateField();
int x = s.getX(); //x will have a value of 5.

Java에서 각 클래스는 최대 하나의 다른 클래스까지 확장 될 수 있습니다.

public class A{}
public class B{}
public class ExtendsTwoClasses extends A, B {} //Illegal

이것은 다중 상속 (multiple inheritance)으로 알려져 있으며 일부 언어에서는 유효하지만 Java는 클래스에서 허용하지 않습니다.

이 결과, 모든 클래스에는 모든 클래스가 내려가는 Object 이어지는 클래스의 비대칭 조상 체인이 있습니다.

상속 및 정적 메서드

자바에서 부모 클래스와 자식 클래스는 모두 같은 이름의 정적 메서드를 가질 수 있습니다. 그러나 이러한 경우 자식 클래스의 정적 메서드 구현은 부모 클래스의 구현을 숨기고 있으므로 메서드 오버라이드가 아닙니다. 예 :

class StaticMethodTest {

  // static method and inheritance
  public static void main(String[] args) {
    Parent p = new Child();
    p.staticMethod(); // prints Inside Parent
    ((Child) p).staticMethod(); // prints Inside Child
  }

  static class Parent {
    public static void staticMethod() {
      System.out.println("Inside Parent");
    }
  }

  static class Child extends Parent {
    public static void staticMethod() {
      System.out.println("Inside Child");
    }
  }
}

정적 메서드는 인스턴스가 아닌 클래스에 바인딩되며이 메서드 바인딩은 컴파일 타임에 발생합니다. staticMethod() 에 대한 첫 번째 호출에서 부모 클래스 참조 p 가 사용 staticMethod() ParentstaticMethod() 버전이 호출됩니다. 두 번째 경우에는 pChild 클래스로, ChildstaticMethod() 실행했다.

가변 섀도 잉

변수는 SHADOWED이고 메소드는 OVERRIDDEN입니다. 어떤 변수가 사용될지는 변수가 선언 된 클래스에 달려 있습니다. 사용할 메소드는 변수가 참조하는 객체의 실제 클래스에 따라 다릅니다.

class Car {
    public int gearRatio = 8;

    public String accelerate() {
        return "Accelerate : Car";
    }
}

class SportsCar extends Car {
    public int gearRatio = 9;

    public String accelerate() {
        return "Accelerate : SportsCar";
    }

    public void test() {

    }


    public static void main(String[] args) {

        Car car = new SportsCar();
        System.out.println(car.gearRatio + "  " + car.accelerate());
        // will print out 8  Accelerate : SportsCar
    }
}

객체 참조의 범위 지정 및 확대

기본 클래스의 인스턴스를 다음과 같이 서브 클래스에 캐스팅합니다. b = (B) a; (보다 구체적인 클래스 객체로 기본 클래스 객체를 좁히기 위해) narrowing 이라고하며 명시 적 타입 캐스트가 필요합니다.

하위 클래스의 인스턴스를 다음과 같이 기본 클래스로 변환합니다. A a = b; 확장 이라고하며 형식 캐스트가 필요하지 않습니다.

설명하기 위해 다음 클래스 선언 및 테스트 코드를 고려하십시오.

class Vehicle {
}

class Car extends Vehicle {
}

class Truck extends Vehicle {
}

class MotorCycle extends Vehicle {
}

class Test {

    public static void main(String[] args) {
    
        Vehicle vehicle = new Car();
        Car car = new Car();        
    
        vehicle = car; // is valid, no cast needed

        Car c = vehicle // not valid
        Car c = (Car) vehicle; //valid
    }
}

Vehicle vehicle = new Car(); 올바른 Java 문입니다. Car 모든 인스턴스는 또한 Vehicle 입니다. 따라서 명시 적 타입 캐스트가 필요없이 할당이 유효합니다.

반면에, Car c = vehicle; 유효하지 않습니다. 의 정지형 vehicle 변수는 Vehicle 이 인스턴스를 참조 할 수 있음을 의미 Car , 트럭 , 오토바이 , or any other current or future subclass of 차량 . (Or indeed, an instance of itself, since we did not declare it as an 추상적 class.) The assignment cannot be allowed, since that might lead to itself, since we did not declare it as an . (Or indeed, an instance of Vehicle itself, since we did not declare it as an . (Or indeed, an instance of class.) The assignment cannot be allowed, since that might lead to . referring to a Truck 인스턴스를 referring to a 자동차로 class.) The assignment cannot be allowed, since that might lead to .

이 상황을 방지하려면 명시 적 타입 캐스트를 추가해야합니다.

Car c = (Car) vehicle;

유형 캐스트는 우리의 가치 기대 컴파일러 알려줍니다 vehicleCar 또는 서브 클래스 Car . 필요한 경우 컴파일러는 런타임 유형 검사를 수행하는 코드를 삽입합니다. 체크가 실패하면 (자), 코드가 실행되면 (자) ClassCastException 가 Throw됩니다.

모든 타입 캐스트가 유효한 것은 아닙니다. 예 :

String s = (String) vehicle;  // not valid

자바 컴파일러는 Vehicle 과 호환되는 유형의 인스턴스가 String 과 유형이 호환 될 수 없다는 것을 알고 있습니다 . 타입 캐스트는 결코 성공할 수 없으며 JLS는 컴파일 오류를 발생 시키도록 명령합니다.

인터페이스 프로그래밍

인터페이스를 프로그래밍하는 배후의 아이디어는 주로 인터페이스에 코드를 기반으로하고 인스턴스화시에만 구체적인 클래스를 사용하는 것입니다. 이런 맥락에서 자바 컬렉션을 다루는 좋은 코드는 다음과 같이 보일 것입니다 (메소드 자체는 전혀 사용되지 않고 단지 일러스트레이션입니다) :

public <T> Set<T> toSet(Collection<T> collection) {
  return Sets.newHashSet(collection);
}

나쁜 코드는 다음과 같이 보일 수 있습니다.

public <T> HashSet<T> toSet(ArrayList<T> collection) {
  return Sets.newHashSet(collection);
}

전자는 광범위한 인수 선택에 적용될 수있을뿐 아니라 결과는 인터페이스에 프로그래밍하는 개념을 일반적으로 준수하는 다른 개발자가 제공하는 코드와 더 잘 호환됩니다. 그러나 전자를 사용하는 가장 중요한 이유는 다음과 같습니다.

  • 대부분의 경우 결과가 사용되는 컨텍스트는 구체적인 구현이 제공하는 많은 세부 사항을 필요로하지 않으며 필요하지도 않습니다.
  • 인터페이스를 고수하면 더 깨끗한 코드가되고 적은 수의 해킹이 특정 시나리오를 제공하는 클래스에 추가됩니다.
  • 코드는 인터페이스가 쉽게 조롱 할 수 있기 때문에 더 테스트 가능합니다.
  • 마지막으로이 개념은 오직 하나의 구현 만이 기대된다하더라도 (적어도 테스트 가능성에 대해서는) 도움이된다.

그렇다면 특정 구현을 염두에두고 새 코드를 작성할 때 어떻게 프로그래밍의 개념을 인터페이스에 쉽게 적용 할 수 있습니까? 우리가 일반적으로 사용하는 한 가지 옵션은 다음과 같은 패턴의 조합입니다.

  • 인터페이스 프로그래밍
  • 공장
  • 건축업자

이러한 원칙에 기반한 다음 예제는 여러 프로토콜에 대해 작성된 RPC 구현의 간소화되고 잘린 버전입니다.

public interface RemoteInvoker {
  <RQ, RS> CompletableFuture<RS> invoke(RQ request, Class<RS> responseClass);
}

위의 인터페이스는 팩토리를 통해 직접 인스턴스화되지 않아야합니다. 대신 HTTP 호출과 AMQP에 대한 구체적인 인터페이스를 파생시킵니다. 각 인터페이스는 인스턴스를 생성하는 팩터 리와 빌더를 가지며 차례로 인스턴스를 생성합니다. 위의 인터페이스 :

public interface AmqpInvoker extends RemoteInvoker {
  static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
    return new AmqpInvokerBuilder(instanceId, factory);
  }
}

AMQP와 함께 사용하기위한 RemoteInvoker 인스턴스는 이제 빌더에 따라 (또는 빌더에 따라 더 복잡하게) 구성 할 수 있습니다.

RemoteInvoker invoker = AmqpInvoker.with(instanceId, factory)
  .requestRouter(router)
  .build();

요청의 호출은 다음과 같이 쉽습니다.

Response res = invoker.invoke(new Request(data), Response.class).get();

Java 8은 정적 메서드를 인터페이스에 직접 배치 할 수 있기 때문에 중간의 팩토리는 AmqpInvoker.with() 로 대체 된 위의 코드에서 암시 적으로 AmqpInvoker.with() 합니다. 버전 8 이전의 Java에서는 내부 Factory 클래스를 사용하여 동일한 효과를 얻을 수 있습니다.

public interface AmqpInvoker extends RemoteInvoker {
  class Factory {
    public static AmqpInvokerBuilder with(String instanceId, ConnectionFactory factory) {
      return new AmqpInvokerBuilder(instanceId, factory);
    }
  }
}

해당 인스턴스는 다음과 같이 바뀝니다.

RemoteInvoker invoker = AmqpInvoker.Factory.with(instanceId, factory)
  .requestRouter(router)
  .build();

위에서 사용 된 빌더는 이와 같이 보일 수 있습니다 (실제 매개 변수가 기본값에서 벗어나는 최대 15 개의 매개 변수를 정의 할 수 있으므로 단순화되었지만). 구문은 public이 아니므로 위의 AmqpInvoker 인터페이스에서만 사용할 수 있습니다.

public class AmqpInvokerBuilder {
  ...
  AmqpInvokerBuilder(String instanceId, ConnectionFactory factory) {
    this.instanceId = instanceId;
    this.factory = factory;
  }

  public AmqpInvokerBuilder requestRouter(RequestRouter requestRouter) {
    this.requestRouter = requestRouter;
    return this;
  }

  public AmqpInvoker build() throws TimeoutException, IOException {
    return new AmqpInvokerImpl(instanceId, factory, requestRouter);
  }
}

일반적으로 빌더는 FreeBuilder와 같은 도구를 사용하여 생성 할 수도 있습니다.

마지막으로,이 인터페이스의 표준 (및 유일한 예상) 구현은 인터페이스, 팩토리 및 빌더의 사용을 강제하는 패키지 로컬 클래스로 정의됩니다.

class AmqpInvokerImpl implements AmqpInvoker {
  AmqpInvokerImpl(String instanceId, ConnectionFactory factory, RequestRouter requestRouter) {
    ...
  }

  @Override
  public <RQ, RS> CompletableFuture<RS> invoke(final RQ request, final Class<RS> respClass) {
    ...
  }
}

한편이 패턴은 기능이 얼마나 단순하거나 복잡하지 않더라도 모든 새 코드를 개발하는 데 매우 효율적이라는 것이 입증되었습니다.

추상 클래스 및 인터페이스 사용법 : "Is-a"관계 vs "Has-a"기능

추상 클래스를 사용하는 경우 : 여러 관련 객체간에 동일하거나 다른 동작을 구현하려면

인터페이스를 사용하는 경우 : 관련이없는 여러 객체로 계약을 구현하는 경우

추상 클래스는 "관계"를 만들고 인터페이스는 "있는"관계를 만듭니다.

아래 코드에서 확인할 수 있습니다.

public class InterfaceAndAbstractClassDemo{
    public static void main(String args[]){
        
        Dog dog = new Dog("Jack",16);
        Cat cat = new Cat("Joe",20);
            
        System.out.println("Dog:"+dog);
        System.out.println("Cat:"+cat);
        
        dog.remember();
        dog.protectOwner();
        Learn dl = dog;
        dl.learn();
                
        cat.remember();
        cat.protectOwner();
        
        Climb c = cat;
        c.climb();
        
        Man man = new Man("Ravindra",40);
        System.out.println(man);
        
        Climb cm = man;
        cm.climb();
        Think t = man;
        t.think();
        Learn l = man;
        l.learn();
        Apply a = man;
        a.apply();
    }
}

abstract class Animal{
    String name;
    int lifeExpentency;
    public Animal(String name,int lifeExpentency ){
        this.name = name;
        this.lifeExpentency=lifeExpentency;
    }
    public abstract void remember();
    public abstract void protectOwner();
    
    public String toString(){
        return this.getClass().getSimpleName()+":"+name+":"+lifeExpentency;
    }
}
class Dog extends Animal implements Learn{
    
    public Dog(String name,int age){
        super(name,age);
    }
    public void remember(){
        System.out.println(this.getClass().getSimpleName()+" can remember for 5 minutes");
    }
    public void protectOwner(){
        System.out.println(this.getClass().getSimpleName()+ " will protect owner");
    }
    public void learn(){
        System.out.println(this.getClass().getSimpleName()+ " can learn:");
    }
}
class Cat extends Animal implements Climb {
    public Cat(String name,int age){
        super(name,age);
    }
    public void remember(){
        System.out.println(this.getClass().getSimpleName() + " can remember for 16 hours");
    }
    public void protectOwner(){
        System.out.println(this.getClass().getSimpleName()+ " won't protect owner");
    }
    public void climb(){
        System.out.println(this.getClass().getSimpleName()+ " can climb");
    }
}
interface Climb{
    void climb();
}
interface Think {
    void think();
}

interface Learn {
    void learn();
}
interface Apply{
    void apply();
}

class Man implements Think,Learn,Apply,Climb{
    String name;
    int age;

    public Man(String name,int age){
        this.name = name;
        this.age = age;
    }
    public void think(){
        System.out.println("I can think:"+this.getClass().getSimpleName());
    }
    public void learn(){
        System.out.println("I can learn:"+this.getClass().getSimpleName());
    }
    public void apply(){
        System.out.println("I can apply:"+this.getClass().getSimpleName());
    }
    public void climb(){
        System.out.println("I can climb:"+this.getClass().getSimpleName());
    }
    public String toString(){
        return "Man :"+name+":Age:"+age;
    }
}

산출:

Dog:Dog:Jack:16
Cat:Cat:Joe:20
Dog can remember for 5 minutes
Dog will protect owner
Dog can learn:
Cat can remember for 16 hours
Cat won't protect owner
Cat can climb
Man :Ravindra:Age:40
I can climb:Man
I can think:Man
I can learn:Man
I can apply:Man

주요 메모 :

  1. AnimalnamelifeExpectancy 및 abstract 메소드 : remember()protectOwner() 와 같은 공유 속성을 가진 추상 클래스입니다. DogCatremember()protectOwner() 메소드를 구현 한 Animals 입니다.

  2. Catclimb() 수는 있지만 Dog 는 할 수 없습니다. Dogthink() 수 있지만 Cat 는 할 수 없습니다. 이러한 특정 기능은 구현을 통해 CatDog 에 추가됩니다.

  3. ManAnimal 아니지만 Think 하고 Learn Apply 하고 Climb 있습니다.

  4. CatMan 아니지만 Climb 할 수 있습니다.

  5. DogMan 아니지만 그것은 Learn

  6. ManCatDog 아니지만 Animal , Cat 또는 Dog 연장하지 않으면 후자의 능력 중 일부를 가질 수 있습니다. 이것은 인터페이스로 수행됩니다.

  7. Animal 은 추상 클래스이지만 인터페이스와 달리 생성자가 있습니다.

TL; DR :

비 관련 클래스는 인터페이스를 통해 기능을 가질 수 있지만 관련 클래스는 기본 클래스의 확장을 통해 비헤이비어를 변경합니다.

특정 사용 사례에서 사용할 설명서를 이해하려면 Java 설명서 페이지 를 참조하십시오.

다음과 같은 경우 추상 클래스 사용을 고려하십시오 .

  1. 밀접하게 관련된 여러 클래스간에 코드를 공유하려고합니다.
  2. 당신은 추상 클래스를 확장하는 클래스는 많은 일반적인 메소드 나 필드를 갖거나 public 이외의 액세스 수정 자 (예 : protected 및 private)를 필요로한다고 예상합니다.
  3. 비 정적 또는 비 최종 필드를 선언하려고합니다.

다음과 같은 경우 인터페이스 사용을 고려하십시오 .

  1. 관련이없는 클래스가 인터페이스를 구현할 것으로 기대합니다. 예를 들어, 관련이없는 많은 객체가 Serializable 인터페이스를 구현할 수 있습니다.
  2. 특정 데이터 유형의 비헤이비어를 지정하려고하지만 해당 비헤이비어를 구현하는 사람은 신경 쓰지 않습니다.
  3. 유형의 다중 상속을 이용하고자합니다.

상속에서 재정의

상속에서 재정의 (override)는 하위 클래스의 수퍼 클래스에서 이미 정의 된 메서드를 사용할 때 사용되지만 수퍼 클래스에서 원래 메서드가 어떻게 디자인되었는지와 다른 방식으로 사용됩니다. 재정의 (Overriding) 기능을 통해 사용자는 기존 자료를 사용하여 코드를 재사용하고 사용자의 요구에 맞게 수정할 수 있습니다.


다음의 예는, ClassB 가, 인쇄 메소드로부터 송신되는 것을 변경하는 것에 의해 ClassA 의 기능을 오버라이드 (override)하는 방법을 나타냅니다.

예:

public static void main(String[] args) {
    ClassA a = new ClassA();
    ClassA b = new ClassB();
    a.printing();
    b.printing();
}

class ClassA {
    public void printing() {        
        System.out.println("A");
    }
}

class ClassB extends ClassA {
    public void printing() {
         System.out.println("B");
    }
}

산출:

에이



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow