자바 오버라이드 vs 숨기기-혼동


88

재정의가 Java에서 숨기는 것과 어떻게 다른지 혼란 스럽습니다. 누구든지 이것이 어떻게 다른지에 대한 자세한 내용을 제공 할 수 있습니까? Java Tutorial을 읽었 지만 샘플 코드가 여전히 혼란 스러웠습니다.

더 명확하게 말하자면 재정의를 잘 이해합니다. 내 문제는 하나가 인스턴스 수준에 있고 다른 하나가 클래스 수준에 있다는 사실을 제외하고는 숨어있는 것이 얼마나 다른지 알 수 없다는 것입니다.

Java 튜토리얼 코드 살펴보기 :

public class Animal {
    public static void testClassMethod() {
        System.out.println("Class" + " method in Animal.");
    }
    public void testInstanceMethod() {
        System.out.println("Instance " + " method in Animal.");
    }
}

그런 다음 하위 클래스가 있습니다 Cat.

public class Cat extends Animal {
    public static void testClassMethod() {
        System.out.println("The class method" + " in Cat.");
    }
    public void testInstanceMethod() {
        System.out.println("The instance method" + " in Cat.");
    }

    public static void main(String[] args) {
        Cat myCat = new Cat();
        Animal myAnimal = myCat;
        Animal.testClassMethod();
        myAnimal.testInstanceMethod();
    }
}

그런 다음 그들은 말합니다.

이 프로그램의 출력은 다음과 같습니다.

Animal의 클래스 방법.

Cat의 인스턴스 메서드.

나에게 클래스 testClassMethod()에서 직접 클래스 메서드를 호출하면 클래스에서 메서드가 Animal실행 된다는 사실 Animal은 매우 분명하며 특별한 것은 없습니다. 그런 다음 testInstanceMethod()참조에서 를 호출 myCat하므로 실행 된 메서드가의 인스턴스에있는 메서드라는 것이 다시 분명합니다 Cat.

내가보기에, 콜 숨김은 오버 라이딩과 똑같이 작동하는데, 왜 그렇게 구별합니까? 위의 클래스를 사용하여이 코드를 실행하면 :

Cat.testClassMethod();

나는 얻을 것이다 : Cat. 그러나 testClassMethod()Cat에서 제거하면 Animal의 클래스 메서드를 얻을 수 있습니다.

이것은 부모와 동일한 서명을 가진 정적 메서드를 하위 클래스에서 거의 재정의하는 것을 보여줍니다.

내가 혼란스럽고 누군가가 빛을 발할 수있는 곳을 분명히하고 있기를 바랍니다. 미리 감사드립니다!


답변:


103

재정의는 기본적으로 후기 바인딩을 지원합니다. 따라서 어떤 메서드가 호출 될지 런타임에 결정됩니다. 비 정적 메서드 용입니다.

숨기기는 다른 모든 멤버 (정적 메서드, 인스턴스 멤버, 정적 멤버)를위한 것입니다. 초기 바인딩을 기반으로합니다. 보다 명확하게 호출하거나 사용할 메서드 또는 멤버는 컴파일 시간에 결정됩니다.

귀하의 예에서 첫 번째 호출 Animal.testClassMethod()static메서드에 대한 호출 이므로 어떤 메서드가 호출 될지 꽤 확실합니다.

두 번째 호출 인 myAnimal.testInstanceMethod()에서는 비 정적 메서드를 호출합니다. 이것이 바로 런타임 다형성이라고 부르는 것입니다. 어떤 메소드를 호출할지 런타임까지 결정되지 않습니다.

자세한 내용은 Overriding Vs Hiding을 참조하십시오 .


3
빠른 답변에 감사드립니다. 이것은 명확합니다! JavaRanch 예제에서 클래스를 직접 사용하는 대신 변수를 사용하여 클래스 메서드를 호출하여 이해하기 쉽도록했습니다. Java 튜토리얼에서 인스턴스를 사용하여 정적 메서드를 호출하는 것은 아마도 좋은 습관이 아니기 때문에 클래스를 직접 사용 했다고 생각하지만 Animal.testClassMethod () 대신 myAnimal.testClassMethod ()사용해야 합니다.
Lostlinkpr

예가 아니라 말로 제대로 적어 놓을 수 있다는 점에 +1! :)
WhyNotHugo

@Kazekage Gaara 과부하와 숨기기 사이에 차이가 있습니까?
gstackoverflow

1
물론 대답에 동의하지만 어떻 private methods습니까? 그들은 수 없습니다 overridden서브 클래스가 자신의 존재에 대해 알고하지 않기 때문에 따라서 그들은 수 있습니다 .. hidden대신.
Paschalis

훌륭한 답변! 완전성을 위해 coderanch에 예제를 추가 할 수도 있지만 :)
Shubham Mittal

19

정적 메서드는 숨겨지고 비 정적 메서드는 재정의됩니다. 호출이 "something ()"과 "this.something ()"으로 한정되지 않은 경우 차이가 두드러집니다.

나는 정말로 그것을 단어에 적어 놓을 수없는 것처럼 보인다. 그래서 여기에 예가있다.

public class Animal {

    public static void something() {
        System.out.println("animal.something");
    }

    public void eat() {
        System.out.println("animal.eat");
    }

    public Animal() {
        // This will always call Animal.something(), since it can't be overriden, because it is static.
        something();
        // This will call the eat() defined in overriding classes.
        eat();
    }

}


public class Dog extends Animal {

    public static void something() {
        // This method merely hides Animal.something(), making it uncallable, but does not override it, or alter calls to it in any way.
        System.out.println("dog.something");
    }

    public void eat() {
        // This method overrides eat(), and will affect calls to eat()
        System.out.println("dog.eat");
    }

    public Dog() {
        super();
    }

    public static void main(String[] args) {
        new Dog();
    }

}

산출:

animal.something
dog.eat

1
좋아, 내가`dog husky = new dog (); '를 호출하면 어떻게 될까? 그리고 husky.Animal();그것이 animal.something 또는 dog.something을 인쇄 할까요 ? 나는 그것의 추측 ** 틀린 말 것을 **이 항상 Animal.something ()를 호출합니다
amarnath harish

@amarnathharish 당신은 할 수 없습니다 .Animal(), 기억 Animal()은 생성자입니다.
Dude156

궁금한 사람을위한 추가 설명으로 something()in Animal() always call의 Animal이하 something()는 이유는 정적 메서드에 대한 호출이 런타임이 아닌 컴파일 타임에 해결되기 때문입니다. 이것은 정적 메서드 호출 Animal()이 항상 암시 적으로를 호출 한다는 것을 의미합니다 Animal.something(). 생각해 보면 매우 직관적입니다. 정적 메서드에 className.staticMethodName()대한 호출은 호출이 동일한 클래스에 있지 않는 한 클래스 이름 (예 :)이 선행되어야합니다 .
Dude156

14

이것이 오버라이드와 숨김의 차이점입니다.

  1. 부모 클래스와 자식 클래스의 메서드가 모두 인스턴스 메서드이면 재정의를 호출했습니다.
  2. 부모 클래스와 자식 클래스의 메서드가 모두 정적 메서드 인 경우 숨김이라고합니다.
  3. 한 메서드는 부모에서 정적 일 수없고 자식에서 인스턴스로 사용할 수 없습니다. 반대의 경우도 마찬가지입니다.

여기에 이미지 설명 입력


3
OP가 이해하는 데 도움이되지 않는다고 말한 튜토리얼에서 직접 테이블을 잘라내어 붙여 넣었습니다.
wolfcastle

이 표는 모든 사례가 고려되지 않은 예에서 매우 명확합니다.
tutak

3

내가 귀하의 질문을 제대로 이해했다면 대답은 "이미 무시하고 있습니다"입니다.

"부모와 동일한 이름을 가진 정적 메서드를 하위 클래스에서 작성하는 것이 거의 오버라이드를 수행한다는 것을 보여줍니다."

슈퍼 클래스의 메서드와 정확히 동일한 이름을 가진 메서드를 서브 클래스에 작성하면 슈퍼 클래스의 메서드가 재정의됩니다. 메서드를 재정의하는 데 @Override 주석이 필요하지 않습니다. 그러나 코드를 더 읽기 쉽게 만들고 컴파일러가 실제로 메서드를 재정의하고 있는지 (예를 들어 하위 클래스 메서드를 잘못 입력하지 않았는지) 확인하도록합니다.


1
이 답변은 재정의 / 숨기기와 관련하여 인스턴스 대 정적 메서드를 처리하지 못합니다.
Paul Bellora

3

재정의는 인스턴스 메서드에서만 발생합니다. 참조 변수의 유형이 Animal이고 객체가 Cat이면 Cat에서 인스턴스 메서드가 호출됩니다 (이는 재정의 됨). 동일한 acat 객체에 대해 Animal 클래스 메서드가 사용됩니다.

public static void main(String[] args) {
    Animal acat = new Cat();
    acat.testInstanceMethod();
    acat.testClassMethod();

}

출력은 다음과 같습니다.

The instance method in Cat.
Class method in Animal.

2
public class First {

    public void Overriding(int i) {  /* will be overridden in class Second */ }

    public static void Hiding(int i) {  /* will be hidden in class Second
                                           because it's static */ }
}


public class Second extends First {

    public void Overriding(int i) {  /* overridden here */  }

    public static void Hiding(int i) {  /* hides method in class First
                                           because it's static */ } 
}

암기 규칙은 간단합니다. 확장 클래스의 메서드는 static을 void로 변경할 수없고 void를 static으로 변경할 수 없습니다. 컴파일 오류의 원인이됩니다.

그러나 void Name변경 되면 void Name재정의입니다.

그리고 static Name변경 되면 static NameHiding입니다. (메소드를 호출하는 데 사용되는 참조 유형에 따라 하위 클래스의 정적 메서드와 수퍼 클래스 중 하나를 모두 호출 할 수 있습니다.)


1

이 코드 조각에서는 '정적'대신 '개인'액세스 한정자를 사용하여 메서드 숨기기와 메서드 재정의의 차이점을 보여줍니다.

class Animal {
// Use 'static' or 'private' access modifiers to see how method hiding work.
private void testInstancePrivateMethod(String source) {
    System.out.println("\tAnimal: instance Private method calling from "+source);
}
public void testInstanceMethodUsingPrivateMethodInside() {
    System.out.println("\tAnimal: instance Public method with using of Private method.");
    testInstancePrivateMethod( Animal.class.getSimpleName() );
}

// Use default, 'protected' or 'public' access modifiers to see  how method overriding work.
protected void testInstanceProtectedMethod(String source) {
    System.out.println("\tAnimal: instance Protected method calling from "+source);
}
public void testInstanceMethodUsingProtectedMethodInside() {
    System.out.println("\tAnimal: instance Public method with using of Protected method.");
    testInstanceProtectedMethod( Animal.class.getSimpleName() );
  } 
}  


public class Cat extends Animal {
private void testInstancePrivateMethod(String source) {
    System.out.println("Cat: instance Private method calling from " + source );
}
public void testInstanceMethodUsingPrivateMethodInside() {
    System.out.println("Cat: instance Public method with using of Private method.");
    testInstancePrivateMethod( Cat.class.getSimpleName());
    System.out.println("Cat: and calling parent after:");
    super.testInstanceMethodUsingPrivateMethodInside();
}

protected void testInstanceProtectedMethod(String source) {
    System.out.println("Cat: instance Protected method calling from "+ source );
}
public void testInstanceMethodUsingProtectedMethodInside() {
    System.out.println("Cat: instance Public method with using of Protected method.");
    testInstanceProtectedMethod(Cat.class.getSimpleName());
    System.out.println("Cat: and calling parent after:");
    super.testInstanceMethodUsingProtectedMethodInside();
}

public static void main(String[] args) {
    Cat myCat = new Cat();
    System.out.println("----- Method hiding -------");
    myCat.testInstanceMethodUsingPrivateMethodInside();
    System.out.println("\n----- Method overriding -------");
    myCat.testInstanceMethodUsingProtectedMethodInside();
}
}

산출:

----- Method hiding -------
Cat: instance Public method with using of Private method.
Cat: instance Private method calling from Cat
Cat: and calling parent after:
   Animal: instance Public method with using of Private method.
   Animal: instance Private method calling from Animal

----- Method overriding -------
Cat: instance Public method with using of Protected method.
Cat: instance Protected method calling from Cat
Cat: and calling parent after:
   Animal: instance Public method with using of Protected method.
Cat: instance Protected method calling from Animal

왜 그것이 upvotes를 얻지 않았는지 궁금합니다.
amarnath harish

0

최근 Java 연구를 기반으로

  • 메서드 재정의 , 하위 클래스에 동일한 서명이있는 동일한 메서드가 하위 클래스에있을 때.
  • 메서드 숨김 , 하위 클래스에 동일한 메서드 이름이 있지만 매개 변수가 다른 경우. 이 경우 부모 메서드를 재정의하는 것이 아니라 숨 깁니다.

OCP Java 7 책, 70-71 페이지의 예 :

public class Point {
  private int xPos, yPos;
  public Point(int x, int y) {
        xPos = x;
        yPos = y;
  }

  public boolean equals(Point other){
  .... sexy code here ...... 
  }

  public static void main(String []args) {
   Point p1 = new Point(10, 20);
   Point p2 = new Point(50, 100);
   Point p3 = new Point(10, 20);
   System.out.println("p1 equals p2 is " + p1.equals(p2));
   System.out.println("p1 equals p3 is " + p1.equals(p3));
   //point's class equals method get invoked
  }
}

그러나 다음과 같은 주요 내용을 작성하면 :

  public static void main(String []args) {
   Object p1 = new Point(10, 20);
   Object p2 = new Point(50, 100);
   Object p3 = new Point(10, 20);
   System.out.println("p1 equals p2 is " + p1.equals(p2));
   System.out.println("p1 equals p3 is " + p1.equals(p3));
   //Object's class equals method get invoked
  }

두 번째 메인에서는 Object 클래스를 정적 ​​유형으로 사용하므로 Point 객체에서 equal 메서드를 호출 할 때 Point 클래스가 매개 변수로 도착하기를 기다리고 있지만 Object가옵니다. 그래서 Object 클래스는 실행되는 메소드와 같습니다. 왜냐하면 거기에 equals (Object o)가 있기 때문입니다. 이 경우 Point의 클래스 같음은 재정의되지 않지만 Object 클래스 같음 메서드를 숨 깁니다 .


0
public class Parent {

  public static void show(){
    System.out.println("Parent");
  }
}

public class Child extends Parent{

  public static void show(){
    System.out.println("Child");
  }
}

public class Main {

public static void main(String[] args) {
    Parent parent=new Child();
    parent.show(); // it will call parent show method
  }
}

// We can call static method by reference ( as shown above) or by using class name (Parent.show())

0

링크 된 자바 튜토리얼 페이지는 재정의 및 숨기기의 개념을 설명합니다.

동일한 시그니처 (이름, 매개 변수의 수 및 유형) 및 반환 유형을 가진 하위 클래스의 인스턴스 메서드는 슈퍼 클래스의 인스턴스 메서드와 같은 방식으로 슈퍼 클래스의 메서드를 재정의합니다.

서브 클래스가 슈퍼 클래스의 정적 메서드와 동일한 시그니처를 사용하여 정적 메서드를 정의하는 경우 서브 클래스의 메서드는 슈퍼 클래스의 메서드를 숨 깁니다.

정적 메서드를 숨기는 것과 인스턴스 메서드를 재정의하는 것의 차이점은 다음과 같은 중요한 의미를 갖습니다.

  1. 호출되는 재정의 된 인스턴스 메서드의 버전은 하위 클래스에있는 것입니다.
  2. 호출되는 숨겨진 정적 메서드의 버전은 상위 클래스에서 호출되는지 서브 클래스에서 호출되는지에 따라 다릅니다.

귀하의 예로 돌아갑니다.

Animal myAnimal = myCat;

 /* invokes static method on Animal, expected. */
 Animal.testClassMethod(); 

 /* invokes child class instance method (non-static - it's overriding) */
 myAnimal.testInstanceMethod();

위의 진술은 아직 숨어있는 것을 보여주지 않습니다.

이제 다른 출력을 얻으려면 아래 코드를 변경하십시오.

  Animal myAnimal = myCat;
  
  /* Even though myAnimal is Cat, Animal class method is invoked instead of Cat method*/
  myAnimal.testClassMethod();
  
  /* invokes child class instance method (non-static - it's overriding) */
  myAnimal.testInstanceMethod();

나는 숨어라는 단어가 무엇을 의미하는지 이해하려고 노력하고 있습니다. 무엇을 숨기고 있습니까? 부모 클래스의 정적 메서드가 호출되기 때문에 숨겨져 있습니까? 아니면 호출되지 않기 때문에 Child 클래스의 정적 메서드가 숨겨져 있습니까?
전갈

0

위에 나열된 예제 외에도 숨기기와 재정의의 차이점을 명확히하기위한 작은 샘플 코드가 있습니다.

public class Parent {

    // to be hidden (static)
    public static String toBeHidden() {
        return "Parent";
    }

    // to be overridden (non-static)
    public String toBeOverridden() {
        return "Parent";
    }

    public void printParent() {
        System.out.println("to be hidden: " + toBeHidden());
        System.out.println("to be overridden: " + toBeOverridden());
    }
}

public class Child extends Parent {

    public static String toBeHidden() {
        return "Child";
    }

    public String toBeOverridden() {
        return "Child";
    }

    public void printChild() {
        System.out.println("to be hidden: " + toBeHidden());
        System.out.println("to be overridden: " + toBeOverridden());
    }
}

public class Main {

    public static void main(String[] args) {
        Child child = new Child();
        child.printParent();
        child.printChild();
    }
}

child.printParent()출력 호출 :
숨김 :
재정의 할 부모 : 자식

child.printChild()출력 호출 :
숨김 : 아이를
오버라이드 (override) 할 수 : 아이

위의 출력 (특히 굵게 표시된 출력)에서 볼 수 있듯이 메서드 숨김은 재정의와 다르게 동작합니다.

Java는 메서드에 대해서만 숨기기 및 재정의를 허용합니다. 동일한 규칙이 변수에 적용되지 않습니다. 변수 재정의는 허용되지 않으므로 변수는 숨길 수만 있습니다 (정적 변수와 비 정적 변수 간의 차이 없음). 아래 예는 메서드 getName()가 재정의되고 변수 name가 숨겨 지는 방법을 보여줍니다 .

public class Main {

    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.name); // prints Parent (since hiding)
        System.out.println(p.getName()); // prints Child (since overriding)
    }
}

class Parent {
    String name = "Parent";

    String getName() {
        return name;
    }
}

class Child extends Parent {
    String name = "Child";

    String getName() {
        return name;
    }
}

0

런타임시 재정의 된 메서드의 자식 버전은 메서드 호출이 부모 또는 자식 클래스 메서드에서 정의되었는지 여부에 관계없이 인스턴스에 대해 항상 실행됩니다. 이러한 방식으로 ParentClassName.method () 구문을 사용하여 부모 메서드에 대한 명시 적 호출이 참조되지 않는 한 부모 메서드는 사용되지 않습니다. 또는 런타임에 메서드 호출이 부모 클래스에 정의되어 있으면 숨겨진 메서드의 부모 버전이 항상 실행됩니다.


0

In 메서드 재정의 에서 메서드 확인은 런타임 개체를 기반으로 JVM에 의해 수행됩니다. 메서드 숨김에서 메서드 확인은 참조를 기반으로 컴파일러에 의해 수행됩니다. 그러므로,

코드가 다음과 같이 작성 되었다면

public static void main(String[] args) {
        Animal myCat = new Cat();        
        myCat.testClassMethod();        
    }

출력은 다음과 같습니다 :
동물의 클래스 메서드.


0

하위 클래스에 동일한 정적 메서드가있을 때 컴파일러가 슈퍼 클래스 메서드 구현을 숨기므로 숨김이라고합니다.

컴파일러는 재정의 된 메서드에 대한 제한된 가시성을 갖지 않으며 런타임 중에 만 사용되는 메서드를 결정합니다.


0

재정의와 숨기기의 차이점은 다음과 같습니다.

동물 a = new Cat ();

a.testClassMethod ()는 메서드 숨김의 예이므로 부모 클래스의 메서드를 호출합니다. 호출 할 메서드는 참조 변수의 유형에 따라 결정되며 컴파일 타임에 결정됩니다.

a.testInstanceMethod ()는 메서드 재정의의 예이므로 자식 클래스의 메서드를 호출합니다. 호출 할 메서드는 런타임에 메서드를 호출하는 데 사용되는 개체에 의해 결정됩니다.


-1

자바에서 정적 메소드 숨김이 어떻게 발생합니까? Cat 클래스는 Animal 클래스를 확장하고 있습니다. 따라서 Cat 클래스에는 정적 메소드가 모두 있습니다 (즉, Child 클래스의 정적 메소드와 Parent 클래스의 정적 메소드를 의미합니다).하지만 JVM은 Parent 정적 메소드를 어떻게 숨길까요? 힙과 스택에서 어떻게 처리되고 있습니까?


이것은 대답이 아닙니다. 질문의 확장입니다. 별도의 질문 자체이거나 질문에 대한 의견의 일부일 수 있습니다.
Sri9911
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.