programing

Java에서는 정적 메서드의 덮어쓰기를 허용하지 않는 이유는 무엇입니까?

bestcode 2022. 8. 9. 21:44
반응형

Java에서는 정적 메서드의 덮어쓰기를 허용하지 않는 이유는 무엇입니까?

정적 메서드를 재정의할 수 없는 이유는 무엇입니까?

가능하다면 예를 들어주세요.

재정의는 클래스의 인스턴스가 있는지 여부에 따라 달라집니다.다형성의 요점은 클래스를 서브클래스로 분류할 수 있다는 것입니다.이러한 서브클래스를 구현하는 오브젝트는 슈퍼클래스에서 정의된(및 서브클래스에서 덮어쓰기됨) 동일한 메서드에 대해 다른 동작을 합니다.스태틱 메서드는 클래스의 어떤 인스턴스에도 관련되어 있지 않기 때문에 이 개념은 적용되지 않습니다.

Java의 설계에 영향을 준 두 가지 고려사항이 있습니다.하나는 퍼포먼스에 대한 우려였다.Smalltalk가 너무 느리다는 비판(쓰레기 컬렉션과 다형성 콜도 그 일부)이 많았으며, Java의 크리에이터는 그것을 피하기로 결심했다.또 다른 하나는 Java의 대상 사용자가 C++ 개발자라는 결정이었습니다.정적 메서드를 사용하는 방법은 C++ 프로그래머에게 친숙하다는 장점이 있었고, 호출할 메서드를 결정하기 위해 런타임까지 기다릴 필요가 없기 때문에 매우 빨랐습니다.

개인적으로 자바 디자인의 결점이라고 생각합니다.예, 예, 비정적 메서드는 인스턴스에, 정적 메서드는 클래스 등에 첨부되는 것으로 알고 있습니다.다만, 다음의 코드를 고려해 주세요.

public class RegularEmployee {
    private BigDecimal salary;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        return salary.multiply(getBonusMultiplier());
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

이 코드는 예상대로 작동하지 않습니다.즉, Special Employee는 일반 직원과 마찬가지로 2%의 보너스를 받습니다.하지만 "스태틱"을 제거하면 스페셜 직원은 3%의 보너스를 받게 됩니다.

(인정하건대, 이 예는 실제에서는 보너스 승수를 하드 코딩이 아닌 데이터베이스 어딘가에 두는 것이 바람직하지 않은 코딩 스타일입니다.그러나 그것은 단지 요점과 무관한 많은 코드로 인해 사례를 중단시키고 싶지 않았기 때문입니다.)

getBonusMultiplier를 스태틱으로 하는 것이 좋을 것 같습니다.각 범주에 직원의 인스턴스(instance)가 없어도 모든 범주에 대한 보너스 승수를 표시할 수 있습니다.이러한 예를 검색해 봐야 무슨 의미가 있을까요?새로운 카테고리의 종업원을 만들고 있는데, 아직 그 카테고리에 할당된 종업원이 없는 경우는 어떻게 됩니까?이것은 논리적으로 매우 정적인 함수입니다.

하지만 효과가 없어요.

그리고 네, 네, 위의 코드를 고쳐 쓸 수 있는 방법은 얼마든지 생각할 수 있습니다.내 요점은 그것이 해결할 수 없는 문제를 만든다는 것이 아니라, 그것은 무의식적인 프로그래머에게 함정을 만든다는 것이다. 왜냐하면 언어는 합리적인 사람이 예상하는 것처럼 행동하지 않기 때문이다.

OOP 언어용 컴파일러를 작성하려고 하면 정적 함수를 덮어쓸 수 있도록 컴파일러를 구현하는 것이 어렵거나 불가능한 이유를 금방 알 수 있을 것입니다.

또는 Java가 이렇게 행동하는 데는 타당한 이유가 있을 수 있습니다.이 행동의 장점, 즉 이로 인해 더 쉬워지는 문제의 카테고리를 지적할 수 있는 사람이 있습니까?단순히 자바 언어 사양을 가리키며 "이거 봐, 이게 어떻게 동작하는지 문서화되어 있어"라고 말하지 마.나는 그것을 알고 있습니다.하지만 이런 식으로 행동해야 하는 타당한 이유가 있을까요? ('바르게 하는 것은 너무 어려웠다'는 명백한 것 말고도)

갱신하다

@VicKirk:만약 이것이 Java가 스태틱스를 처리하는 방식에 맞지 않기 때문에 "나쁜 설계"라는 의미라면, 제 대답은 "물론이죠"입니다.원래 게시글에서 말씀드렸듯이, 작동이 안 됩니다.하지만 이것이 기능하는 언어, 즉 가상 기능처럼 통계학이 무시될 수 있는 언어, 즉 모호한 요소가 도입되거나 효율적인 구현이 불가능하다는 의미에서의 잘못된 디자인이라는 의미라면, 저는 "왜?"라고 대답합니다.컨셉이 뭐가 문제죠?"

제가 예로 든 것은 매우 자연스럽게 하고 싶은 일이라고 생각합니다.인스턴스 데이터에 의존하지 않는 함수를 가진 클래스가 있으며 인스턴스 메서드 내에서 호출할 뿐만 아니라 인스턴스로부터 독립적으로 호출할 수도 있습니다.왜 이게 안 되는 거죠?나는 몇 년 동안 이런 상황에 꽤 많이 부딪쳤다.실제로 함수를 가상으로 만들고, 그 다음 더미 인스턴스를 사용하여 가상 메서드에 콜을 전달하는 정적 메서드가 유일한 목적이 되는 정적 메서드를 만듭니다.그것은 그곳에 가는 매우 우회적인 방법인 것 같다.

간단히 말하면, 그것은 완전히 가능하지만, Java는 그것을 하지 않는다.

다음은 Java의 현재 상황을 나타내는 코드입니다.

★★Base.java:

package sp.trial;
public class Base {
  static void printValue() {
    System.out.println("  Called static Base method.");
  }
  void nonStatPrintValue() {
    System.out.println("  Called non-static Base method.");
  }
  void nonLocalIndirectStatMethod() {
    System.out.println("  Non-static calls overridden(?) static:");
    System.out.print("  ");
    this.printValue();
  }
}

★★Child.java:

package sp.trial;
public class Child extends Base {
  static void printValue() {
    System.out.println("  Called static Child method.");
  }
  void nonStatPrintValue() {
    System.out.println("  Called non-static Child method.");
  }
  void localIndirectStatMethod() {
    System.out.println("  Non-static calls own static:");
    System.out.print("  ");
    printValue();
  }
  public static void main(String[] args) {
    System.out.println("Object: static type Base; runtime type Child:");
    Base base = new Child();
    base.printValue();
    base.nonStatPrintValue();
    System.out.println("Object: static type Child; runtime type Child:");
    Child child = new Child();
    child.printValue();
    child.nonStatPrintValue();
    System.out.println("Class: Child static call:");
    Child.printValue();
    System.out.println("Class: Base static call:");
    Base.printValue();
    System.out.println("Object: static/runtime type Child -- call static from non-static method of Child:");
    child.localIndirectStatMethod();
    System.out.println("Object: static/runtime type Child -- call static from non-static method of Base:");
    child.nonLocalIndirectStatMethod();
  }
}

Mac에서 Java 1.6을 사용하여 실행한 경우(Iclipse에서 실행한 경우:

Object: static type Base; runtime type Child.
  Called static Base method.
  Called non-static Child method.
Object: static type Child; runtime type Child.
  Called static Child method.
  Called non-static Child method.
Class: Child static call.
  Called static Child method.
Class: Base static call.
  Called static Base method.
Object: static/runtime type Child -- call static from non-static method of Child.
  Non-static calls own static.
    Called static Child method.
Object: static/runtime type Child -- call static from non-static method of Base.
  Non-static calls overridden(?) static.
    Called static Base method.

여기에서는, 최초의 케이스로서 서프라이즈(질문에 관한 것)를 생각할 수 있는 유일한 케이스는 다음과 같습니다.

「은, 실행 시간」)와 되지 않습니다 객체 인스턴스를 사용하여 호출된 경우에도 마찬가지입니다.obj.staticMethod()

마지막 케이스:

클래스의 객체 메서드 내에서 정적 메서드를 호출할 때 선택한 정적 메서드는 객체의 런타임 유형을 정의하는 클래스가 아니라 클래스 자체에서 액세스할 수 있는 메서드입니다.

오브젝트 인스턴스를 사용한 호출

스태틱 콜은 컴파일 시에 해결되지만, 스태틱하지 않은 메서드콜은 런타임에 해결됩니다.스태틱 메서드는 (부모로부터) 상속되지만 (자녀에 의해) 덮어쓰지는 않습니다.그렇지 않으면 깜짝 놀랄 수도 있습니다.

오브젝트 메서드 내에서 호출

오브젝트 메서드콜은 런타임유형을 사용하여 해결되지만 스태틱(클래스) 메서드콜은 컴파일 타임(선언)유형을 사용하여 해결됩니다.

규칙 변경

규칙들을 을 " " " " 로 변경한다.Child.printValue()컴파일러가 오브젝트(또는 컨텍스트)의 선언된 클래스를 사용하여 콜을 컴파일할 때 해결하는 것이 아니라 정적 콜에 실행 시 유형을 제공해야 합니다.그 후 스태틱콜은 현재의 오브젝트 메서드콜과 마찬가지로 (다이나믹) 타입의 계층을 사용하여 콜을 해결할 수 있습니다.

이것은 간단하게 실행할 수 있습니다(Java:-O를 변경했을 경우).또한 전혀 불합리하지 않습니다만, 몇 가지 흥미로운 고려사항이 있습니다.

주된 고려사항은 어떤 스태틱메서드 콜이 이것을 실행할지를 결정해야 한다는 것입니다.

현재 자바어에는 다음과 같은 "quirk"가 있습니다.obj.staticMethod()이 '콜'로 ObjectClass.staticMethod()콜(경고 포함)[주의: ObjectClass입니다.obj 할 수 은 .]입니다.실행시간 타입의obj.

이렇게 하면 메서드 본문을 읽기 어려워집니다.부모 클래스의 스태틱콜은 동적으로 「재루팅」될 가능성이 있습니다.이를 회피하려면 클래스 이름을 사용하여 스태틱메서드를 호출해야 합니다.이것에 의해, 콜은(지금과 같이) 컴파일 타임 타입의 계층에 의해서 명확하게 해결됩니다.

은 더. 즉, 보다 까다롭습니다.this.staticMethod()이어야 합니다.와 같은 뜻이어야 한다.obj.staticMethod() 을 사용합니다.this는 데코레이션이 (로컬이 ) 호출하는 수 있습니다 프로그램은 거의 「」, 「」에 합니다.」라고 하는 입니다.this.method()를 참조해 주세요.

'되지 않은 콜'은 요?staticMethod()현재와 마찬가지로 로컬 클래스의 컨텍스트를 사용하여 무엇을 할지 결정할 것을 권장합니다.그렇지 않으면 큰 혼란이 뒤따를 것이다., 은 ★★★★★★★★★★★★★★★★★★★★★★★.method(),를합니다.this.method()method비정적 방식입니다.ThisClass.method()method적적메메 메다다다다다이것은 또 다른 혼란의 원인이다.

기타 고려사항

한 경우( 가능성이 있습니다), """의 .final,private ★★★★★★★★★★★★★★★★★」protected에 대한 예선으로서static는 '우리'가 '우리'가 '우리'가 '우리'가 '에 익숙해져야 한다.private static ★★★★★★★★★★★★★★★★★」public final메서드는 재정의되지 않으므로 컴파일 시 안전하게 해결할 수 있으며 로컬 참조로 읽어도 "안전"합니다.

사실 우리가 틀렸다.
기본적으로는 Java에서는 정적 메서드를 재정의할 수 없지만 Java의 Class 및 Method 클래스의 문서를 자세히 살펴보면 다음과 같은 회피책을 통해 정적 메서드를 재정의할 수 있는 방법을 찾을 수 있습니다.

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;

class RegularEmployee {

    private BigDecimal salary = BigDecimal.ONE;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }
    public BigDecimal calculateBonus() {
        return salary.multiply(this.getBonusMultiplier());
    }
    public BigDecimal calculateOverridenBonus() {
        try {
            // System.out.println(this.getClass().getDeclaredMethod(
            // "getBonusMultiplier").toString());
            try {
                return salary.multiply((BigDecimal) this.getClass()
                    .getDeclaredMethod("getBonusMultiplier").invoke(this));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
    // ... presumably lots of other code ...
}

final class SpecialEmployee extends RegularEmployee {

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

public class StaticTestCoolMain {

    static public void main(String[] args) {
        RegularEmployee Alan = new RegularEmployee();
        System.out.println(Alan.calculateBonus());
        System.out.println(Alan.calculateOverridenBonus());
        SpecialEmployee Bob = new SpecialEmployee();
        System.out.println(Bob.calculateBonus());
        System.out.println(Bob.calculateOverridenBonus());
    }
}

결과 출력:

0.02
0.02
0.02
0.03

달성하려고 했던 것:)

세 번째 변수 Carl을 RegularEmployee로 선언하고 SpecialEmployee의 인스턴스를 할당하더라도 첫 번째 경우에는 RegularEmployee 메서드 호출이 두 번째 경우에는 SpecialEmployee 메서드가 호출됩니다.

RegularEmployee Carl = new SpecialEmployee();

System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());

출력 콘솔을 확인합니다.

0.02
0.03

;)

정적 메서드는 JVM에 의해 글로벌하게 처리되며 개체 인스턴스에 바인딩되지 않습니다.

개념적으로는 클래스 오브젝트(Smalltalk 등의 언어)에서 정적 메서드를 호출할 수 있다면 가능하지만 Java에서는 그렇지 않습니다.

편집

정적 방식을 과부하할 수 있습니다.그러나 클래스는 퍼스트 클래스 객체가 아니기 때문에 정적 메서드를 재정의할 수 없습니다.리플렉션을 사용하여 런타임에 객체의 클래스를 가져올 수 있지만 얻은 객체는 클래스 계층과 평행하지 않습니다.

class MyClass { ... }
class MySubClass extends MyClass { ... }

MyClass obj1 = new MyClass();
MySubClass obj2 = new MySubClass();

ob2 instanceof MyClass --> true

Class clazz1 = obj1.getClass();
Class clazz2 = obj2.getClass();

clazz2 instanceof clazz1 --> false

반추는 가능하지만, 거기서 그칩니다.clazz1.staticMethod()단, , , , " " 를 사용합니다.MyClass.staticMethod()에 의 .this 않다super정적인 방법으로.정적 방법은 전역 함수이기 때문에 다형성의 개념도 없기 때문에 메서드 덮어쓰기는 의미가 없습니다.

하지만 이것은 만약MyClass는 Smalltalk에서와 같이 메서드를 호출하는 런타임 객체입니다(또는 JRuby가 시사하는 것처럼 JRuby에 대해서는 아무것도 모릅니다).

, 네, 요.이렇게 정적 오브젝트할 수 .obj1.staticMethod()하지만 그 통사적인 설탕은MyClass.staticMethod()피해야 합니다.IDE를 사용하다왜 이런 지름길을 허용했는지 모르겠어요.

메서드 덮어쓰기는 동적 디스패치에 의해 가능합니다.즉, 선언된 오브젝트 유형은 동작을 결정하는 것이 아니라 런타임 유형을 결정합니다.

Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"

다 그렇더라도lassie ★★★★★★★★★★★★★★★★★」kermit의 되어 있다.Animal 「」, 「」( 「」).speak())는 다이내믹디스패치가 메서드콜만 바인드하기 때문에 달라집니다..speak()컴파일 시간이 아닌 실행 시 구현으로 이동합니다.

자, 여기 요.staticstatic은 dynamic의 반의어입니다.스태틱 메서드를 덮어쓸 수 없는 이유는 스태틱멤버에 다이내믹 디스패치가 없기 때문입니다.스태틱은 문자 그대로 '비동적'을 의미하기 때문입니다.다이내믹하게 디스패치된 경우(따라서 오버라이드될 수 있음)static키워드는 더 이상 의미가 없습니다.

네. 실질적으로 Java는 Static 메서드를 덮어쓸 수 있습니다.이론적으로 No는 Java에서 Static 메서드를 덮어쓸 경우 컴파일 및 실행은 원활하지만 Java의 기본 속성인 Polymorphism을 잃게 됩니다.컴파일 및 실행을 직접 시도할 수 없는 경우 어디서나 읽을 수 있습니다.답을 얻을 수 있을 것입니다.Have Class Animal 및 static 메서드 eat()가 있고 서브클래스의 해당 스태틱메서드를 덮어쓰면 Dog라고 부릅니다.그런 다음 언제 동물 참조에 개 개체를 할당하고 Java Dog's eat()에 따라 eat()을 호출해야 하지만 정적 덮어쓰기 동물 eat()에서는 호출됩니다.

class Animal {
    public static void eat() {
        System.out.println("Animal Eating");
    }
}

class Dog extends Animal{
    public static void eat() {
        System.out.println("Dog Eating");
    }
}

class Test {
    public static void main(String args[]) {
       Animal obj= new Dog();//Dog object in animal
       obj.eat(); //should call dog's eat but it didn't
    }
}


Output Animal Eating

은 Java여야 .Dog Eating.
이치노다형성을 지원하기 위해 Java는 Late Binding을 사용하므로 런타임에만 메서드가 호출되며 정적 메서드의 경우에는 호출되지 않습니다.정적 메서드에서는 컴파일러가 런타임보다 컴파일 시간에 메서드를 호출하기 때문에 오브젝트에 따라 메서드를 취득하지 않고 참조에 따라 메서드를 취득할 수 있습니다.따라서 실제로는 정적 오버링을 지원하지만 이론적으로는 지원하지 않습니다.

Java(및 많은 OOP 언어)에서는 모든 메서드에 고정 시그니처(파라미터와 타입)가 있습니다.메서드에서 첫 으로 오브젝트를 추가합니다. 객체 자체에 대한 참조와 객체 내에서 호출되면 컴파일러는 자동으로 추가합니다.this.

정적 방식에는 차이가 없습니다. 고정 시그니처가 있습니다.단, 메서드 static을 선언함으로써 컴파일러가 시그니처의 선두에 암묵적인 오브젝트 파라미터를 포함해서는 안 된다고 명시되어 있습니다.따라서 이를 호출하는 다른 코드는 스택상의 오브젝트에 대한 참조를 시도해서는 안 됩니다.이렇게 하면 매개 변수가 스택에서 잘못된 위치(1개씩 이동)에 있기 때문에 메서드 실행이 작동하지 않습니다.

가지 「」는 「」, 「」는 「」등).this따라서 해당 개체의 인스턴스에 속하는 힙 내의 모든 항목을 참조할 수 있습니다.그러나 정적 메서드의 경우 전달된 참조가 없으므로 컨텍스트를 알 수 없기 때문에 이 메서드는 개체 변수 및 메서드에 액세스할 수 없습니다.

Java가 정의를 변경하여 모든 메서드(스태틱 또는 가상)에 대해 오브젝트 컨텍스트가 전달되도록 하려면 기본적으로 가상 메서드만 사용합니다.

오퍼레이션에 대한 코멘트에서 물어본 것처럼 이 기능을 원하는 이유와 목적은 무엇입니까?

저는 루비를 잘 모릅니다, OP에서 언급하고 있기 때문에 조사를 했습니다.Ruby 클래스는 정말 특별한 오브젝트이며 새로운 메서드를 (동적으로도) 만들 수 있습니다.클래스는 Java가 아닌 Ruby에서 풀 클래스 객체입니다.이는 Java(또는 C#)를 사용할 때 받아들여야 하는 사항입니다.이것들은 다이내믹한 언어가 아닙니다만, C# 에서는 다이나믹한 형식이 추가되고 있습니다.실제로 루비는 제가 알 수 있는 한 "static" 메서드는 없습니다.이 경우 싱글톤 클래스 오브젝트에 대한 메서드입니다.그런 다음 이 싱글톤을 새 클래스로 덮어쓸 수 있으며 이전 클래스 객체의 메서드는 새 클래스에서 정의된 메서드를 호출합니다(정답?).따라서 원래 클래스의 컨텍스트에서 메서드를 호출해도 원래 통계 정보만 실행되지만 파생 클래스의 메서드를 호출하면 부모 클래스 또는 서브 클래스 중 하나에서 메서드가 호출됩니다.흥미롭고 그 안에서 어떤 가치를 볼 수 있다.그것은 다른 사고 패턴을 취한다.

당신은 자바에서 일하고 있기 때문에 그 방식에 적응할 필요가 있습니다.왜 그랬을까요?사용 가능한 기술과 이해도를 바탕으로 당시의 성능을 향상시키려 했던 것 같습니다.컴퓨터 언어는 끊임없이 발전하고 있습니다.충분히 거슬러 올라가면 OOP 같은 건 없어.미래에는 다른 새로운 아이디어들이 있을 것이다.

편집: 다른 코멘트가 하나 더 있습니다.이제 차이를 알게 되었고, Java/C# 개발자를 직접 만나보니, Ruby와 같은 언어에서 온 사용자라면 Java 개발자가 제공하는 답변이 왜 혼란스러운지 알 수 있었습니다.자바static은 Ruby 와 다릅니다.class★★★★★★, 로 Ruby와 같은 입니다.Java 개발자들은 이것을 이해하는 데 어려움을 겪을 것이고, 반대로 Ruby/Smalltalk와 같은 언어를 주로 사용하는 사람들도 그러할 것이다.Java도 정적 메서드에 대해 다른 방법으로 "class method"를 사용하지만, Ruby에서는 이 용어를 다르게 사용한다는 사실도 매우 혼란스러운 것을 알 수 있습니다.자바 루비루비 자바이것은 C에서 볼 수 있는 오래된 프로시저 스타일의 함수일 뿐입니다.

그나저나, 질문 감사합니다!저는 오늘 수업방법(루비 스타일)에 대해 새로운 것을 배웠습니다.

재정의는 인스턴스 구성원이 다형 동작을 지원하기 위해 예약되어 있습니다. 정적 클래스 구성원은 특정 인스턴스에 속하지 않습니다.대신 스태틱멤버는 클래스에 속하기 때문에 서브클래스는 보호 및 퍼블릭인스턴스 멤버만 상속하고 스태틱멤버는 상속하지 않기 때문에 덮어쓰기는 지원되지 않습니다.대안적 접근 방식을 평가하기 위해 내부 및 연구 공장 및/또는 전략 설계 패턴을 정의할 수 있습니다.

자바에서 오버라이드 메서드가 어떻게 동작해야 하는지 생각해보면 답은 NO입니다.그러나 정적 메서드를 덮어쓰려고 해도 컴파일러 오류가 발생하지 않습니다.즉, 덮어쓰기를 시도해도 Java는 이를 저지하지 않습니다.그러나 비정적 메서드에서 얻을 수 있는 것과 같은 효과를 얻을 수 있는 효과는 분명 없습니다.Java에서 덮어쓰기란 단순히 특정 메서드가 컴파일 시간 유형이 아닌 객체의 실행 시간 유형에 따라 호출된다는 것을 의미합니다(정적 메서드의 덮어쓰기).네...그들이 왜 이상하게 행동하는지 짐작할 수 있나요?클래스 메서드이기 때문에 이러한 메서드에 대한 액세스는 컴파일 시간 유형 정보만 사용하여 컴파일 시간 동안 항상 해결됩니다.오브젝트 레퍼런스를 사용해 액세스 하는 것은 Java의 설계자에 의해서 주어진 여분의 자유일 뿐이며, 그들이 그것을 제한했을 때만 그 관행을 중단하는 것은 결코 생각해서는 안 된다:-)

: static 메서드를 덮어쓰려고 하면 어떻게 되는지 알아보겠습니다.-

class SuperClass {
// ......
public static void staticMethod() {
    System.out.println("SuperClass: inside staticMethod");
}
// ......
}

public class SubClass extends SuperClass {
// ......
// overriding the static method
public static void staticMethod() {
    System.out.println("SubClass: inside staticMethod");
}

// ......
public static void main(String[] args) {
    // ......
    SuperClass superClassWithSuperCons = new SuperClass();
    SuperClass superClassWithSubCons = new SubClass();
    SubClass subClassWithSubCons = new SubClass();

    superClassWithSuperCons.staticMethod();
    superClassWithSubCons.staticMethod();
    subClassWithSubCons.staticMethod();
    // ...
}
}

출력:-
SuperClass: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod

출력의 두 번째 줄에 주목해 주세요.staticMethod가 덮어쓰기된 경우 이 행은 세 번째 행과 동일해야 합니다.실행시 타입 오브젝트의 staticMethod()는 'SuperClass'가 아니라 'SubClass'로 호출됩니다.이를 통해 정적 메서드는 항상 컴파일 시간 유형 정보만을 사용하여 해결됩니다.

저는 Jay의 코멘트를 좋아하고 두 배로 늘립니다(https://stackoverflow.com/a/2223803/1517187)).
자바어
이전 코멘트에서 보듯이 다른 많은 언어에서는 스태틱메서드 덮어쓰기를 지원하고 있습니다.Jay(Jay) Java(Java)
Dellphi(Object Pascal)는 Java 이전에 OOP를 구현한 언어 중 하나이며 상용 애플리케이션 개발에 사용된 최초의 언어 중 하나입니다.
그 언어는 과거에 상업용 GUI 제품을 쓰는 유일한 언어였기 때문에 많은 사람들이 그 언어를 경험했을 것이다.그리고 - 네, 델파이에서는 정적 방법을 무시할 수 있습니다.실제로 델파이의 정적 메서드는 "클래스 메서드"라고 불리는 반면 델파이는 "델파이의 정적 메서드"라는 다른 개념을 가지고 있었는데, 이는 초기 바인딩된 메서드였다.레이트 바인딩을 사용해야 했던 메서드를 재정의하려면 "가상" 디렉티브를 선언합니다.그래서 매우 편리하고 직관적이어서 자바에서 기대할 수 있습니다.

일반적으로 정적 메서드의 '오버라이드'를 허용하는 것은 의미가 없습니다. 런타임에 호출할 메서드를 결정하는 좋은 방법이 없기 때문입니다.Employee의 예를 들어 Regular Employee.getBonusMultiplier()를 호출하면 어떤 메서드가 실행되어야 합니까?

Java의 경우 객체 인스턴스를 통해 호출되는 한 정적 메서드를 덮어쓸 수 있는 언어 정의를 상상할 수 있습니다.단, 이 모든 것은 정규 클래스 메서드를 재실장하는 것으로, 실제로는 이점을 추가하지 않고 언어에 용장성을 추가하는 것입니다.

덮어쓰기를 통해 객체 유형에 따라 다형성을 생성할 수 있습니다.정적 메서드는 개체와 관련이 없습니다.따라서 Java는 정적 메서드 덮어쓰기를 지원할 수 없습니다.

덮어쓰기를 통해 동적 다형성을 얻을 수 있습니다.정적 방식을 재정의한다고 하면 사용하려는 단어가 모순됩니다.

정적 - 컴파일 시간, 재정의는 동적 다형성에 사용됩니다.둘 다 성격상 반대이기 때문에 함께 사용할 수 없습니다.

동적 다형성 동작은 프로그래머가 객체를 사용하여 인스턴스 메서드에 액세스할 때 발생합니다.JRE는 사용하고 있는 오브젝트의 종류에 따라 다른 클래스의 다양한 인스턴스 메서드를 매핑합니다.

static 메서드 덮어쓰기라고 하면 클래스 이름을 사용하여 액세스하기 때문에 실행 시 메서드를 static 메서드와 링크하는 개념은 없습니다.따라서 정적 방법을 재정의하는 용어 자체는 아무런 의미가 없습니다.

주의: 오브젝트를 사용하여 클래스 메서드에 액세스해도 Java 컴파일러는 이를 검출할 수 있을 만큼 인텔리전트하며 정적 링크를 수행합니다.

정적 방법을 덮어쓰는 것이 무슨 소용이 있습니까?인스턴스를 통해 정적 메서드를 호출할 수 없습니다.

MyClass.static1()
MySubClass.static1()   // If you overrode, you have to call it through MySubClass anyway.

EDIT : 언어설계를 잘못하여 인스턴스를 통해 스태틱메서드를 호출할 수 있는 것 같습니다.일반적으로 아무도 그렇게 하지 않는다.제 잘못이에요.

Java에서 덮어쓰기란 단순히 특정 메서드가 컴파일 시간 유형이 아닌 객체의 런타임 유형에 따라 호출된다는 것을 의미합니다(덮어쓰기된 정적 메서드의 경우).static 메서드는 클래스 메서드이므로 인스턴스 메서드가 아니기 때문에 참조가 어떤 오브젝트 또는 인스턴스를 가리키는지와는 관계가 없습니다.스태틱 메서드는 특정 클래스에 속하기 때문입니다.서브클래스로 다시 선언할 수 있지만 이미 말씀드렸듯이 이 서브클래스는 부모클래스의 스태틱메서드에 대해 아무것도 인식하지 않습니다.이는 부모클래스가 선언된 클래스에만 한정되기 때문입니다.오브젝트 레퍼런스를 사용해 액세스 하는 것은 Java의 설계자에 의해서 주어지는 자유일 뿐입니다.또한 그들이 더 자세한 내용을 제한하고 예를 들어 http://faisalbhagat.blogspot.com/2014/09/method-overriding-and-method-hiding.html를 참조할 때만 그 관행을 중단하는 것을 생각해서는 안 됩니다.

이 질문에 대한 답은 간단합니다.스태틱으로 마크된 메서드 또는 변수는 클래스에만 속하기 때문에 스태틱 메서드는 슈퍼 클래스에만 속하기 때문에 하위 클래스에서 상속할 수 없습니다.

간단한 솔루션:싱글톤 인스턴스를 사용합니다.재정의 및 상속이 허용됩니다.

시스템에는 Singletons Registry 클래스가 있으며, 이 클래스는 통과된 클래스의 인스턴스를 반환합니다.인스턴스를 찾을 수 없으면 인스턴스가 생성됩니다.

Haxe 언어 클래스:

package rflib.common.utils;
import haxe.ds.ObjectMap;



class SingletonsRegistry
{
  public static var instances:Map<Class<Dynamic>, Dynamic>;

  static function __init__()
  {
    StaticsInitializer.addCallback(SingletonsRegistry, function()
    {
      instances = null;
    });

  } 

  public static function getInstance(cls:Class<Dynamic>, ?args:Array<Dynamic>)
  {
    if (instances == null) {
      instances = untyped new ObjectMap<Dynamic, Dynamic>();      
    }

    if (!instances.exists(cls)) 
    {
      if (args == null) args = [];
      instances.set(cls, Type.createInstance(cls, args));
    }

    return instances.get(cls);
  }


  public static function validate(inst:Dynamic, cls:Class<Dynamic>)
  {
    if (instances == null) return;

    var inst2 = instances[cls];
    if (inst2 != null && inst != inst2) throw "Can\'t create multiple instances of " + Type.getClassName(cls) + " - it's singleton!";
  }

}

정적 메서드, 변수, 블록 또는 중첩 클래스는 개체가 아닌 전체 클래스에 속합니다.

Java의 메서드는 객체/클래스의 동작을 노출하는 데 사용됩니다.여기서 방법은 정적(즉, 정적 방법은 클래스의 동작을 나타내기 위해서만 사용됨)이기 때문에 전체 클래스의 동작을 변경/오버라이드하는 것은 객체 지향 프로그래밍의 기본 기둥의 현상, 높은 응집력을 위반합니다.(컨스트럭터는 Java에서 특별한 종류의 메서드입니다.)

높은 응집력 - 하나의 클래스는 하나의 역할만 가져야 합니다.예를 들어 자동차 클래스는 자전거, 트럭, 비행기 등이 아닌 자동차 물체만 생산해야 합니다.그러나 Car 클래스는 자체에만 속하는 일부 기능(동작)을 가질 수 있습니다.

그래서 자바 프로그래밍 언어를 설계하면서.언어 설계자들은 개발자들이 본질적으로 정적인 방법을 만드는 것만으로 클래스의 일부 행동을 혼자만 유지할 수 있도록 한다고 생각했습니다.


다음 코드에서는 정적 메서드를 덮어쓰려고 하지만 컴파일 오류는 발생하지 않습니다.

public class Vehicle {
static int VIN;

public static int getVehileNumber() {
    return VIN;
}}

class Car extends Vehicle {
static int carNumber;

public static int getVehileNumber() {
    return carNumber;
}}

왜냐하면 여기서는 방법을 재정의하는 것이 아니라 단지 다시 선언하는 것이기 때문입니다.Java 에서는, 메서드(스태틱/비정적)의 재선언이 가능합니다.

Car 클래스의 getVeileNumber() 메서드에서 static 키워드를 삭제하면 컴파일 오류가 발생합니다.Vehicle 클래스에만 속하는 static 메서드의 기능을 변경하려고 합니다.

또한 getVeileNumber()가 final로 선언된 경우 final 키워드는 프로그래머가 메서드를 다시 선언하는 것을 제한하므로 코드는 컴파일되지 않습니다.

public static final int getVehileNumber() {
return VIN;     }

전반적으로 정적 방법을 사용하는 장소는 소프트웨어 설계자에게 달려 있습니다.저는 개인적으로 클래스의 인스턴스를 만들지 않고 몇 가지 작업을 수행하기 위해 정적 메서드를 사용하는 것을 선호합니다.둘째, 수업의 행동을 외부로부터 숨기기 위해서입니다.

여기 간단한 설명이 있습니다.스태틱 메서드는 클래스와 관련지어지며 인스턴스 메서드는 특정 오브젝트와 관련지어집니다.오버라이드를 사용하면 특정 객체와 관련된 오버라이드 메서드의 다른 구현을 호출할 수 있습니다.따라서 객체가 아닌 클래스 자체와 연관된 정적 메서드를 우선 재정의하는 것은 직관에 반합니다.따라서 정적 메서드는 호출하는 개체에 따라 재정의할 수 없으며 항상 생성된 클래스와 관련지어집니다.

위의 답변을 보면 정적 메서드를 덮어쓸 수 없다는 것을 알 수 있지만 하위 클래스에서 정적 메서드에 액세스하는 개념을 오해해서는 안 됩니다.

이 스태틱 메서드가 서브클래스에 정의된 새로운 스태틱메서드에 의해 숨겨지지 않은 경우 서브클래스 참조를 사용하여 슈퍼클래스의 스태틱메서드에 액세스 할 수 있습니다.

예를 들어, 아래의 코드를 참조해 주세요.

public class StaticMethodsHiding {
    public static void main(String[] args) {
        SubClass.hello();
    }
}


class SuperClass {
    static void hello(){
        System.out.println("SuperClass saying Hello");
    }
}


class SubClass extends SuperClass {
    // static void hello() {
    // System.out.println("SubClass Hello");
    // }
}

출력:-

SuperClass saying Hello

하위 클래스에서 정적 메서드를 숨기는 방법에 대한 자세한 내용은 Java oracle 문서를 참조하여 하위 클래스에서 수행할 수 있는 작업을 검색하십시오.

고마워요.

다음 코드는 가능한 것을 나타냅니다.

class OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriden Meth");   
}   

}   

public class OverrideStaticMeth extends OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriding Meth");   
}   

public static void main(String[] args) {   
OverridenStaticMeth osm = new OverrideStaticMeth();   
osm.printValue();   

System.out.println("now, from main");
printValue();

}   

} 

언급URL : https://stackoverflow.com/questions/2223386/why-doesnt-java-allow-overriding-of-static-methods

반응형