IT이야기

추상은 아니지만 재정의해야 하는 메서드를 만드는 방법

cyworld 2021. 9. 18. 09:30
반응형

추상은 아니지만 재정의해야 하는 메서드를 만드는 방법이 있습니까?


자식 클래스가 슈퍼 클래스의 비추상 메서드를 재정의하도록 강제하는 방법이 있습니까?

부모 클래스의 인스턴스를 만들 수 있어야 하지만 클래스가 이 클래스를 확장하는 경우 일부 메서드에 대한 자체 정의를 제공해야 합니다.


내가 아는 한 이것을 수행하는 직접적인 컴파일러 시행 방법은 없습니다.

부모 클래스를 인스턴스화 할 수 없도록 하는 대신 기본 구현이 있는 일부(비공개) 하위 클래스의 인스턴스를 생성하는 팩토리 메서드를 제공하여 이 문제를 해결할 수 있습니다 .

public abstract class Base {
  public static Base create() {
    return new DefaultBase();
  }

  public abstract void frobnicate();

  static class DefaultBase extends Base {
    public void frobnicate() {
      // default frobnication implementation
    }
  }
}

지금 수 없지만 기본 구현을 얻으려면 new Base()할 수 있습니다 Base.create().


다른 사람들이 지적했듯이 직접 할 수는 없습니다.

그러나 이를 수행하는 한 가지 방법은 다음 과 같이 전략 패턴 을 사용하는 것입니다.

public class Base {
    private final Strategy impl;

    // Public factory method uses DefaultStrategy
    // You could also use a public constructor here, but then subclasses would
    // be able to use that public constructor instead of the protected one
    public static Base newInstance() {
        return new Base(new DefaultStrategy());
    }

    // Subclasses must provide a Strategy implementation
    protected Base(Strategy impl) {
        this.impl = impl;
    }

    // Method is final: subclasses can "override" by providing a different
    // implementation of the Strategy interface
    public final void foo() {
        impl.foo();
    }

    // A subclass must provide an object that implements this interface
    public interface Strategy {
        void foo();
    }

    // This implementation is private, so subclasses cannot access it
    // It could also be made protected if you prefer
    private static DefaultStrategy implements Strategy {
        @Override
        public void foo() {
            // Default foo() implementation goes here
        }
    }
}

이 방법으로 인터페이스를 만드는 것을 고려하십시오. 클래스 자손이 구현해야 합니다.


가장 쉬운 방법은 기본 클래스에서 상속되는 추상 클래스를 만드는 것입니다.


public class Base {
    public void foo() {
        // original method
    }
}

abstract class BaseToInheritFrom extends Base {
    @Override
    public abstract void foo();
}

class RealBaseImpl extends BaseToInheritFrom {
    @Override
    public void foo() {
        // real impl
    }
}

아니요, 그것이 추상적인 방법의 요점입니다. 사용 사례는 무엇입니까? 아마도 우리는 근본적인 필요에 따라 그것에 대해 생각할 수 있습니다.


방법: 메서드의 기본 구현 내에서 리플렉션을 사용하여 개체의 정확한 클래스를 가져옵니다. 클래스가 기본 클래스와 정확히 일치하지 않으면 RuntimeException 또는 이에 상응하는 것을 throw합니다.

public class Parent {

    public void defaultImpl(){
        if(this.getClass() != Parent.class){
            throw new RuntimeException();
        }
    }

}

있다 이유 가 불가능는!

파생 클래스 는 메서드를 재정의할 때 기본 클래스의 구현을 간단히 호출 할 수 있습니다.

그래서 클래스가 메서드를 재정의하도록 강제하는 요점은 무엇입니까? 이득이 없다고 생각합니다.


대답은 아니오일 것입니다. 템플릿 디자인 패턴을 사용하여 다시 디자인할 수 있습니다. 도움이 될 수 있습니다.

또는 자식 클래스가 인터페이스를 구현하도록 할 수 있습니다. 인터페이스는 수퍼 클래스에 의해 구현되거나 구현되지 않을 수 있습니다.


기본 클래스에 예외를 throw하는 메서드가 항상 있도록 할 수 있습니다.

기술적으로 기본 클래스는 메서드를 정의했지만 메서드를 재정의하지 않고는 사용할 수 없습니다. 이러한 경우 명시적 throw 문이 필요하지 않기 때문에 런타임 예외를 선호합니다. 예는 다음과 같습니다.

public class Parent {

  public void doStuff() {
    throw new RuntimeException("doStuff() must be overridden");
  }

}

public class Child extends Parent {

  @Override
  public void doStuff() {
    ... all is well here ...
  }

}

단점은 이것이 Base객체 생성을 방해하지 않는다는 것입니다 . 그러나 "재정의해야 하는" 방법 중 하나를 사용하려는 사람은 곧 클래스를 재정의해야 한다는 것을 알게 될 것입니다.

이 솔루션은 요청에 대한 설명을 잘 충족하지만 애플리케이션은 이와 같은 솔루션을 필요로 하지 않는 이점이 있을 것입니다. 추상 키워드가 제공하는 컴파일러 검사로 런타임 충돌을 피하는 것이 훨씬 좋습니다.


나는 다른 답변을 미러링하고 파생 클래스가 추상이 아닌 메서드를 재정의하도록 강제하는 컴파일러 강제 방법이 없다고 말합니다. 메서드를 추상화하는 요점은 이 서명이 있는 메서드가 존재해야 하지만 기본 수준에서 지정할 수 없으므로 파생 수준에서 지정해야 한다고 정의하는 것입니다. 기본 수준에서 메서드의 작동하는 중요하지 않은 구현(비어 있지 않고 예외를 throw하거나 메시지를 표시하지 않는 경우)이 있는 경우 다음을 호출하는 데 꼭 필요한 것은 아닙니다. 파생 클래스의 소비자가 성공하는 메서드입니다. 따라서 컴파일러는 기본 또는 파생 수준에서 매우 성공적으로 실행할 수 있는 메서드를 재정의할 필요가 없습니다.

파생 클래스가 작업 구현을 재정의하기를 원하는 상황에서는 기본 구현이 파생 클래스의 소비자가 원하는 작업을 수행하지 않는다는 것이 매우 분명해야 합니다. 기본 클래스에 구현이 충분하지 않거나 잘못된 구현이 있습니다. 그러한 경우에, 당신은 당신의 클래스에서 파생된 프로그래머가 그가 하고 있는 일을 알고 있다는 것을 신뢰해야 하고 따라서 그의 새로운 객체를 사용하는 맥락에서 정답을 생성하지 않기 때문에 메소드를 재정의할 필요가 있다는 것을 알아야 합니다.

나는 당신이 할 수 있는 한 가지를 생각할 수 있습니다. 봉인된(Javahead의 경우 최종) "기본" 구현이 있는 추상 기반이 필요합니다. 이렇게 하면 마치 "기본" 클래스인 것처럼 쉽게 사용할 수 있는 메서드의 기본 구현이 있지만 새 시나리오에 대해 다른 클래스를 정의하려면 추상 클래스로 돌아가야 하고, 따라서 방법을 다시 구현해야 합니다. 이 메소드는 클래스의 유일한 추상적인 것일 수 있으므로 여전히 다른 메소드의 기본 구현을 사용할 수 있습니다.

public abstract class BaseClass
{
   public abstract void MethodYouMustAlwaysOverride();

   public virtual void MethodWithBasicImplementation() { ... }
}

public final class DefaultClass:BaseClass
{
   public override void MethodYouMustAlwaysOverride() { ... }

   //the base implementation of MethodWithBasicImplementation 
   //doesn't have to be overridden
}

...

public class DerivedClass:BaseClass
{
   //Because DefaultClass is final, we must go back to BaseClass,
   //which means we must reimplement the abstract method
   public override void MethodYouMustAlwaysOverride() { ... }

   //again, we can still use MethodWithBasicImplementation, 
   //or we can extend/override it
   public override void MethodWithBasicImplementation() { ... }
}

그러나 여기에는 두 가지 단점이 있습니다. 첫째, 상속을 통해 DefaultClass의 구현에 액세스할 수 없기 때문에 DefaultClass의 구현을 확장할 수 없습니다. 즉, DefaultClass가 하는 일에 더하여 DRY를 위반하는 DefaultClass의 코드를 다시 작성해야 합니다. 둘째, DerivedClass에서 상속을 허용하면 강제로 재정의할 수 없기 때문에 이것은 한 수준의 상속에서만 작동합니다.


아마도 이것이 도움이 될 것입니다.

class SuperClass {

    void doStuff(){

        if(!this.getClass().equals(SuperClass.class)){

            throw new RuntimeException("Child class must implement doStuff Method");
        }else{
            //ok
            //default implementation
        }
    }
}

class Child extends SuperClass{

    @Override
    void doStuff() {
        //ok
    }
}

class Child2 extends SuperClass{

}


 new SuperClass().doStuff(); //ok
 new Child().doStuff();         //ok
 new Child2().doStuff();        //error

좋아, 이렇게 배워보자. Java 스타일 지침을 준수하고 Java 구문을 사용합니다. 따라서 다중 상속 및 C++ 템플릿을 사용할 수 없다고 가정합니다.

부모 클래스 메서드를 추상화하는 것은 필수가 아닙니다. OOP에서는 다형성 개념을 사용합니다. 두 가지 이상의 다른 방법으로 동일한 방법을 사용할 수 있습니다. 이를 메서드 재정의라고 합니다.

예를 들어 보겠습니다.

    public class Animal{
       public void makeSound(){
          System.out.println("Animal doesn't know how to make sound");
       }

    }

    public class PussyCat extends Animal{
        public void makeSound(){

           System.out.println("meowwww !!!");
        }

        public static void main(String args[]){
           PussyCat aCat=new PussyCat();
           aCat.makeSound(); 
        }
    }

이것은 "meowww !!!"를 인쇄할 것입니다. 화면에.

그러나 이것이 makeSound 메소드가 자식 클래스에서 재정의되어야 함을 의미하지는 않습니다.

자식 클래스가 강제로 메서드를 재정의해야 하는 경우 클래스별로 인터페이스를 구현하는 것이 좋습니다.

      public Interface audible{
         public void makeSound();

      }

      public class pussyCat implements audible{
          // now you must implement the body of makeSound method here
          public void makeSound(){
            System.out.println("meowwww !!!"); 
          }

          public static void main(String args[]){
           PussyCat aCat=new PussyCat();
           aCat.makeSound(); 
        }
      }

이것은 또한 "meowwww !!!"를 인쇄할 것입니다. 화면에


권장되지는 않지만 메소드 구현에서 예외(MethodeMustBeOverRiddenExp와 같은)를 던질 수 있습니다. 물론 RunTime 강제이지만 nuthing보다 나을 수 있습니다.

ReferenceURL : https://stackoverflow.com/questions/7832444/is-there-a-way-to-make-a-method-which-is-not-abstract-but-must-be-overridden

반응형