Java에서는 super.super.method()가 허용되지 않는 이유는 무엇입니까?
나는 이 질문을 읽고 다음과 같이 쓸 수 있다면 쉽게 풀릴 수 있을 것이라고는 말할 수 없다.
@Override
public String toString() {
return super.super.toString();
}
많은 경우에 도움이 되는지는 모르겠지만, 왜 도움이 되지 않는지, 또 다른 언어에도 이런 것이 있는지 궁금합니다.
여러분 어떻게 생각하세요?
편집: 명확하게 하자면, Java에서는 불가능하다는 것을 알고 있으며, 저는 그것을 그다지 그리워하지 않습니다.이것은 전혀 기대하지 않았던 동작으로 컴파일러 에러가 나서 놀랐습니다.방금 아이디어가 떠올랐는데 의논하고 싶어요.
캡슐화를 위반합니다.부모 클래스의 행동을 무시할 수 없습니다.경우에 따라서는 (특히 같은 메서드 내에서) 자신의 클래스 행동을 우회할 수 있지만 부모의 행동은 우회할 수 없습니다.예를 들어 기본 "항목 집합", "빨간색 항목 집합"을 나타내는 하위 클래스 및 "큰 빨간색 항목 집합"을 나타내는 하위 클래스가 있다고 가정합니다.다음과 같은 것이 바람직합니다.
public class Items
{
public void add(Item item) { ... }
}
public class RedItems extends Items
{
@Override
public void add(Item item)
{
if (!item.isRed())
{
throw new NotRedItemException();
}
super.add(item);
}
}
public class BigRedItems extends RedItems
{
@Override
public void add(Item item)
{
if (!item.isBig())
{
throw new NotBigItemException();
}
super.add(item);
}
}
괜찮습니다. Red Items는 포함된 항목이 모두 빨간색임을 항상 확신할 수 있습니다.여기서 super.super.add()를 호출할 수 있었다고 가정합니다.
public class NaughtyItems extends RedItems
{
@Override
public void add(Item item)
{
// I don't care if it's red or not. Take that, RedItems!
super.super.add(item);
}
}
것을 할 수 , 는 '불변수', '불변수', '불변수'입니다.RedItems
장났습습니니다
이해 하셨나요?
나는 존 스키트가 정답을 알고 있다고 생각한다.슈퍼클래스의 슈퍼클래스의 음영 변수에 캐스팅을 통해 접근할 수 있다는 것을 덧붙이고 싶습니다.this
:
interface I { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1 { int x = 2; }
class T3 extends T2 {
int x = 3;
void test() {
System.out.println("x=\t\t" + x);
System.out.println("super.x=\t\t" + super.x);
System.out.println("((T2)this).x=\t" + ((T2)this).x);
System.out.println("((T1)this).x=\t" + ((T1)this).x);
System.out.println("((I)this).x=\t" + ((I)this).x);
}
}
class Test {
public static void main(String[] args) {
new T3().test();
}
}
그러면 다음과 같은 출력이 생성됩니다.
x= 3super.x=2((T2) 이거).x= 2((T1) 이거).x = 1(이것).x = 0
(JLS의 예)
그러나 메서드 호출은 객체의 런타임 유형에 따라 결정되기 때문에 메서드 호출에는 작동하지 않습니다.
다음 코드는 super.super...를 사용할 수 있다고 생각합니다.대부분의 경우 super.super.disc().(그렇게 하는 것이 귀찮아도)
요컨대
- 상위 유형의 임시 인스턴스 생성
- 필드 값을 원래 개체에서 임시 개체로 복사
- 임시 개체에서 대상 메서드 호출
- 수정된 값을 원래 개체로 다시 복사
사용방법:
public class A {
public void doThat() { ... }
}
public class B extends A {
public void doThat() { /* don't call super.doThat() */ }
}
public class C extends B {
public void doThat() {
Magic.exec(A.class, this, "doThat");
}
}
public class Magic {
public static <Type, ChieldType extends Type> void exec(Class<Type> oneSuperType, ChieldType instance,
String methodOfParentToExec) {
try {
Type type = oneSuperType.newInstance();
shareVars(oneSuperType, instance, type);
oneSuperType.getMethod(methodOfParentToExec).invoke(type);
shareVars(oneSuperType, type, instance);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static <Type, SourceType extends Type, TargetType extends Type> void shareVars(Class<Type> clazz,
SourceType source, TargetType target) throws IllegalArgumentException, IllegalAccessException {
Class<?> loop = clazz;
do {
for (Field f : loop.getDeclaredFields()) {
if (!f.isAccessible()) {
f.setAccessible(true);
}
f.set(target, f.get(source));
}
loop = loop.getSuperclass();
} while (loop != Object.class);
}
}
평판이 좋지 않아서 다른 답변에 추가하겠습니다.
Jon Sket은 훌륭한 답변과 함께 아름다운 예를 제시합니다.Matt B의 말은 일리가 있다: 모든 슈퍼클래스가 슈퍼클래스를 가진 것은 아니다.슈퍼가 없는 슈퍼를 부르면 코드가 깨질 거야
객체 지향 프로그래밍(Java)은 함수가 아닌 객체에 관한 것입니다.태스크 지향 프로그래밍을 원하는 경우 C++ 또는 다른 것을 선택하십시오.오브젝트가 슈퍼클래스에 맞지 않는 경우 오브젝트를 "대형 클래스"에 추가하거나 새 클래스를 만들거나 해당 오브젝트가 맞는 다른 슈퍼를 찾아야 합니다.
개인적으로, 저는 이 제한이 Java의 가장 큰 장점 중 하나라고 생각합니다.지금까지 사용한 다른 언어에 비해 코드는 다소 딱딱하지만, 저는 항상 무엇을 기대할 수 있는지 알고 있습니다.이는 Java의 "단순하고 익숙한" 목표에 도움이 됩니다.내 생각에 슈퍼.슈퍼라고 부르는 것은 간단하지도 않고 익숙하지도 않다.개발자도 같은 생각을 했을까요?
이렇게 하는 데는 몇 가지 좋은 이유가 있다.메서드가 잘못 구현되어 있지만 부모 메서드는 올바르게 구현되어 있는 서브클래스가 있을 수 있습니다.서드파티 라이브러리에 속해 있기 때문에 소스를 변경할 수 없거나 변경할 수 없는 경우가 있습니다.이 경우 서브클래스를 만들지만 super.super 메서드를 호출하기 위해1개의 메서드를 덮어씁니다.
다른 포스터에서 알 수 있듯이 성찰을 통해 이것을 할 수 있지만, 다음과 같은 것을 할 수 있어야 한다.
(Super Super Class의 경우).방법();
지금 이 문제에 대처하고 있습니다.빠른 수정은 superclass 메서드를 복사하여 서브서브클래스 메서드에 붙여넣는 것입니다:)
다른 사람들이 한 아주 좋은 점 외에도, 나는 다른 이유가 있다고 생각한다: 만약 슈퍼클래스가 슈퍼클래스를 가지고 있지 않다면?
확장되므로(Object
,super.whatever()
는 항상 슈퍼클래스의 메서드를 참조합니다. 만약 이 확장만 ?Object
요? - 어떻게요?super.super
그때를 참조해 주세요.Null Pointer(늘 포인터)
이것이 허용되지 않는 주된 이유는 캡슐화를 위반하기 때문이라고 생각합니다만, 이것도 작은 이유일 수 있습니다.
를 들어,를 들어, 「For 」 「For 」 「For 」 「For 」 「For 」 「For 」 「For 」 「For 」 「For 」 등),equals
그러면 사실상 항상 다이렉트 슈퍼클래스 버전을 먼저 호출하고 싶을 때 슈퍼클래스 버전을 차례로 호출합니다.
임의의 슈퍼 클래스 버전의 메서드를 호출하는 것은 거의 의미가 없다고 생각합니다(전혀 생각할 수 없습니다).자바에서 그게 가능한지 모르겠어요.C++로 실행할 수 있습니다.
this->ReallyTheBase::foo();
아마 잘 안 쓰니까 그럴 거예요.이 기능을 사용할 수 있는 유일한 이유는 직계 부모가 일부 기능을 오버라이드하여 원래 상태로 복원하려는 경우입니다.
반의 직계부모가 조부모님보다 당신의 반과 더 밀접하게 관련되어 있어야 하기 때문에, 그것은 제가 보기에 OO의 원칙에 어긋나는 것 같습니다.
super.super.method() 호출은 기본 클래스의 코드를 변경할 수 없는 경우에 적합합니다.이 문제는 기존 라이브러리를 확장할 때 자주 발생합니다.
먼저 자신에게 물어보세요, 왜 그 수업을 연장하는 거죠?"변경할 수 없기 때문에"라고 대답할 경우 응용 프로그램에서 정확한 패키지와 클래스를 만들고, 악의적인 메서드를 다시 쓰거나 위임자를 만들 수 있습니다.
package com.company.application;
public class OneYouWantExtend extends OneThatContainsDesiredMethod {
// one way is to rewrite method() to call super.method() only or
// to doStuff() and then call super.method()
public void method() {
if (isDoStuff()) {
// do stuff
}
super.method();
}
protected abstract boolean isDoStuff();
// second way is to define methodDelegate() that will call hidden super.method()
public void methodDelegate() {
super.method();
}
...
}
public class OneThatContainsDesiredMethod {
public void method() {...}
...
}
예를 들어 org.springframework.test.context를 생성할 수 있습니다.Junit 4. 봄응용 프로그램의 JUnit4ClassRunner 클래스가 있으므로 이 클래스는 jar에서 실제 클래스보다 먼저 로드되어야 합니다.그런 다음 메서드 또는 컨스트럭터를 다시 작성합니다.
주의:이것은 절대적인 해킹이며 사용을 강력히 권장하지 않지만 작동 중입니다! 이 접근 방식을 사용하는 것은 클래스 로더에 문제가 있을 수 있기 때문에 위험합니다.또한 덮어쓰기 클래스가 포함된 라이브러리를 업데이트할 때마다 문제가 발생할 수 있습니다.
@John Sket 좋은 설명.IMO 중 누군가가 super.super 메서드를 호출할 경우 직계 부모의 동작을 무시하고 그랜드 부모의 동작에 액세스해야 합니다.이 작업은 Of 인스턴스를 통해 수행할 수 있습니다.아래 코드와 같다
public class A {
protected void printClass() {
System.out.println("In A Class");
}
}
public class B extends A {
@Override
protected void printClass() {
if (!(this instanceof C)) {
System.out.println("In B Class");
}
super.printClass();
}
}
public class C extends B {
@Override
protected void printClass() {
System.out.println("In C Class");
super.printClass();
}
}
여기는 드라이버 클래스입니다.
public class Driver {
public static void main(String[] args) {
C c = new C();
c.printClass();
}
}
이 출력은 다음과 같습니다.
In C Class
In A Class
이 경우 Class B printClass 동작은 무시됩니다.이것이 super.super를 달성하기 위한 이상적인 방법인지 좋은 방법인지는 잘 모르겠지만 여전히 효과가 있다.
이 Github 프로젝트, 특히 objectHandle 변수를 확인합니다.이 프로젝트는 손자에게 조부모 메서드를 실제로 정확하게 호출하는 방법을 보여줍니다.
링크가 끊어질 경우를 대비해서 다음과 같은 코드를 입력합니다.
import lombok.val;
import org.junit.Assert;
import org.junit.Test;
import java.lang.invoke.*;
/*
Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.
Please don't actually do this... :P
*/
public class ImplLookupTest {
private MethodHandles.Lookup getImplLookup() throws NoSuchFieldException, IllegalAccessException {
val field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
field.setAccessible(true);
return (MethodHandles.Lookup) field.get(null);
}
@Test
public void test() throws Throwable {
val lookup = getImplLookup();
val baseHandle = lookup.findSpecial(Base.class, "toString",
MethodType.methodType(String.class),
Sub.class);
val objectHandle = lookup.findSpecial(Object.class, "toString",
MethodType.methodType(String.class),
// Must use Base.class here for this reference to call Object's toString
Base.class);
val sub = new Sub();
Assert.assertEquals("Sub", sub.toString());
Assert.assertEquals("Base", baseHandle.invoke(sub));
Assert.assertEquals(toString(sub), objectHandle.invoke(sub));
}
private static String toString(Object o) {
return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode());
}
public class Sub extends Base {
@Override
public String toString() {
return "Sub";
}
}
public class Base {
@Override
public String toString() {
return "Base";
}
}
}
해피 코딩!!!!
가능하다면 다른 방법으로 super.super method body를 넣고 싶습니다.
class SuperSuperClass {
public String toString() {
return DescribeMe();
}
protected String DescribeMe() {
return "I am super super";
}
}
class SuperClass extends SuperSuperClass {
public String toString() {
return "I am super";
}
}
class ChildClass extends SuperClass {
public String toString() {
return DescribeMe();
}
}
또는 슈퍼 슈퍼클래스를 변경할 수 없는 경우는, 다음과 같이 시험할 수 있습니다.
class SuperSuperClass {
public String toString() {
return "I am super super";
}
}
class SuperClass extends SuperSuperClass {
public String toString() {
return DescribeMe(super.toString());
}
protected String DescribeMe(string fromSuper) {
return "I am super";
}
}
class ChildClass extends SuperClass {
protected String DescribeMe(string fromSuper) {
return fromSuper;
}
}
두 경우 모두
new ChildClass().toString();
'I am super super' 결과
리플렉션을 사용하여 슈퍼클래스의 슈퍼클래스를 적어도 취득할 수 있을 것 같습니다.이것이 도움이 될 것 같다면, http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getSuperclass()의 Javadoc을 검토해 주세요.
public class A {
@Override
public String toString() {
return "A";
}
}
public class B extends A {
@Override
public String toString() {
return "B";
}
}
public class C extends B {
@Override
public String toString() {
return "C";
}
}
public class D extends C {
@Override
public String toString() {
String result = "";
try {
result = this.getClass().getSuperclass().getSuperclass().getSuperclass().newInstance().toString();
} catch (InstantiationException ex) {
Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
}
return result;
}
}
public class Main {
public static void main(String... args) {
D d = new D();
System.out.println(d);
}
}
실행: 빌드 성공(총 시간: 0초)
아키텍처가 여러 파생 클래스를 대신하여 구현되는 공통 CustomBaseClass에 공통 기능을 구축하는 경우 이와 같은 상황이 있었습니다.단, 특정 파생 클래스의 특정 메서드에 대한 공통 로직을 우회할 필요가 있습니다.이 경우 super.super.methodX 구현을 사용해야 합니다.
이를 실현하기 위해서는 CustomBaseClass에 부울 멤버를 도입합니다.부울 멤버는 커스텀 구현을 선택적으로 연기하고 필요에 따라 기본 프레임워크 구현으로 전환할 수 있습니다.
...
FrameworkBaseClass (....) extends...
{
methodA(...){...}
methodB(...){...}
...
methodX(...)
...
methodN(...){...}
}
/* CustomBaseClass overrides default framework functionality for benefit of several derived classes.*/
CustomBaseClass(...) extends FrameworkBaseClass
{
private boolean skipMethodX=false;
/* implement accessors isSkipMethodX() and setSkipMethodX(boolean)*/
methodA(...){...}
methodB(...){...}
...
methodN(...){...}
methodX(...){
if (isSkipMethodX()) {
setSKipMethodX(false);
super.methodX(...);
return;
}
... //common method logic
}
}
DerivedClass1(...) extends CustomBaseClass
DerivedClass2(...) extends CustomBaseClass
...
DerivedClassN(...) extends CustomBaseClass...
DerivedClassX(...) extends CustomBaseClass...
{
methodX(...){
super.setSKipMethodX(true);
super.methodX(...);
}
}
그러나 앱뿐만 아니라 프레임워크에서도 우수한 아키텍처 원칙을 준수하면 isA 방식이 아닌 hasA 방식을 사용하여 이러한 상황을 쉽게 피할 수 있었습니다.그러나 항상 제대로 설계된 아키텍처를 기대하는 것은 매우 실용적이지 않기 때문에 견고한 설계 원칙에서 벗어나 이와 같은 해커를 도입할 필요가 있습니다.내 2센트만...
,를 실현하는 입니다.super.super.sayYourName()
(자바)
public class GrandMa {
public void sayYourName(){
System.out.println("Grandma Fedora");
}
}
public class Mama extends GrandMa {
public void sayYourName(boolean lie){
if(lie){
super.sayYourName();
}else {
System.out.println("Mama Stephanida");
}
}
}
public class Daughter extends Mama {
public void sayYourName(boolean lie){
if(lie){
super.sayYourName(lie);
}else {
System.out.println("Little girl Masha");
}
}
}
public class TestDaughter {
public static void main(String[] args){
Daughter d = new Daughter();
System.out.print("Request to lie: d.sayYourName(true) returns ");
d.sayYourName(true);
System.out.print("Request not to lie: d.sayYourName(false) returns ");
d.sayYourName(false);
}
}
출력:
Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha
저는 이것이 상속 약정을 어기는 문제라고 생각합니다.
으로써 그 으로써, 「동작」의 특징이 됩니다.
, 「」를 호출할 super.super.method()
이치
슈퍼 클래스에서는 체리 픽을 할 수 없습니다.
하다, 하다, 하다, 하다, 하다, 하다, 하다를 할 수 .super.super.method()
코드 - 나쁜 디자인 사인, 코드 상속! - 나쁜 디자인 사인!
슈퍼 및 슈퍼 클래스를 리팩터링할 수 없는 경우(일부 레거시 코드) 상속보다 구성을 선택합니다.
캡슐화 해제란 캡슐화된 코드를 해제하여 일부 메서드를 덮어쓰는 것입니다.덮어쓰지 않도록 설계된 메서드는 최종 마크가 붙어 있습니다.
C#에서는 다음과 같은 상위 메서드를 호출할 수 있습니다.
public class A
internal virtual void foo()
...
public class B : A
public new void foo()
...
public class C : B
public new void foo() {
(this as A).foo();
}
델파이에서도 이 작업을 수행할 수 있습니다.
type
A=class
procedure foo;
...
B=class(A)
procedure foo; override;
...
C=class(B)
procedure foo; override;
...
A(objC).foo();
그러나 Java에서는 이러한 초점을 어떤 기어에서만 수행할 수 있습니다.생각할 수 있는 방법 중 하나는 다음과 같습니다.
class A {
int y=10;
void foo(Class X) throws Exception {
if(X!=A.class)
throw new Exception("Incorrect parameter of "+this.getClass().getName()+".foo("+X.getName()+")");
y++;
System.out.printf("A.foo(%s): y=%d\n",X.getName(),y);
}
void foo() throws Exception {
System.out.printf("A.foo()\n");
this.foo(this.getClass());
}
}
class B extends A {
int y=20;
@Override
void foo(Class X) throws Exception {
if(X==B.class) {
y++;
System.out.printf("B.foo(%s): y=%d\n",X.getName(),y);
} else {
System.out.printf("B.foo(%s) calls B.super.foo(%s)\n",X.getName(),X.getName());
super.foo(X);
}
}
}
class C extends B {
int y=30;
@Override
void foo(Class X) throws Exception {
if(X==C.class) {
y++;
System.out.printf("C.foo(%s): y=%d\n",X.getName(),y);
} else {
System.out.printf("C.foo(%s) calls C.super.foo(%s)\n",X.getName(),X.getName());
super.foo(X);
}
}
void DoIt() {
try {
System.out.printf("DoIt: foo():\n");
foo();
Show();
System.out.printf("DoIt: foo(B):\n");
foo(B.class);
Show();
System.out.printf("DoIt: foo(A):\n");
foo(A.class);
Show();
} catch(Exception e) {
//...
}
}
void Show() {
System.out.printf("Show: A.y=%d, B.y=%d, C.y=%d\n\n", ((A)this).y, ((B)this).y, ((C)this).y);
}
}
objC.DoIt() 결과 출력:
DoIt: foo():
A.foo()
C.foo(C): y=31
Show: A.y=10, B.y=20, C.y=31
DoIt: foo(B):
C.foo(B) calls C.super.foo(B)
B.foo(B): y=21
Show: A.y=10, B.y=21, C.y=31
DoIt: foo(A):
C.foo(A) calls C.super.foo(A)
B.foo(A) calls B.super.foo(A)
A.foo(A): y=11
Show: A.y=11, B.y=21, C.y=31
그것은 간단히 하기 쉽다.예:
B의 C 서브클래스와 A의 B 서브클래스예를 들어, 둘 다 methodName()을 사용합니다.
public abstract class A {
public void methodName() {
System.out.println("Class A");
}
}
public class B extends A {
public void methodName() {
super.methodName();
System.out.println("Class B");
}
// Will call the super methodName
public void hackSuper() {
super.methodName();
}
}
public class C extends B {
public static void main(String[] args) {
A a = new C();
a.methodName();
}
@Override
public void methodName() {
/*super.methodName();*/
hackSuper();
System.out.println("Class C");
}
}
실행 클래스 C 출력: 클래스 A 클래스 C
출력 대신:클래스 A 클래스 B 클래스 C
슈퍼클래스가 필요하다고 생각되는 경우 해당 클래스의 변수에 해당 클래스를 참조할 수 있습니다.예를 들어 다음과 같습니다.
public class Foo
{
public int getNumber()
{
return 0;
}
}
public class SuperFoo extends Foo
{
public static Foo superClass = new Foo();
public int getNumber()
{
return 1;
}
}
public class UltraFoo extends Foo
{
public static void main(String[] args)
{
System.out.println(new UltraFoo.getNumber());
System.out.println(new SuperFoo().getNumber());
System.out.println(new SuperFoo().superClass.getNumber());
}
public int getNumber()
{
return 2;
}
}
출력:
2
1
0
public class SubSubClass extends SubClass {
@Override
public void print() {
super.superPrint();
}
public static void main(String[] args) {
new SubSubClass().print();
}
}
class SuperClass {
public void print() {
System.out.println("Printed in the GrandDad");
}
}
class SubClass extends SuperClass {
public void superPrint() {
super.print();
}
}
출력: GrandDad로 인쇄
키워드 super는 슈퍼클래스에서 메서드를 호출하는 방법일 뿐입니다.Java 튜토리얼:https://docs.oracle.com/javase/tutorial/java/IandI/super.html
메서드가 슈퍼클래스의 메서드 중 하나를 재정의하는 경우 키워드 super를 사용하여 재정의된 메서드를 호출할 수 있습니다.
슈퍼 오브젝트의 레퍼런스라고 믿지 마세요!!!아니요, 슈퍼클래스에서 메서드를 호출하는 키워드입니다.
다음은 예를 제시하겠습니다.
class Animal {
public void doSth() {
System.out.println(this); // It's a Cat! Not an animal!
System.out.println("Animal do sth.");
}
}
class Cat extends Animal {
public void doSth() {
System.out.println(this);
System.out.println("Cat do sth.");
super.doSth();
}
}
전화할 때cat.doSth()
, 방법doSth()
수업 중에Animal
인쇄하다this
그리고 고양이입니다.
언급URL : https://stackoverflow.com/questions/586363/why-is-super-super-method-not-allowed-in-java
'programing' 카테고리의 다른 글
재사용 가능한 VueJS 컴포넌트에서의 테일윈드 CSS 클래스 덮어쓰기 (0) | 2022.06.14 |
---|---|
Vim을 Java IDE로 사용하기 위한 힌트 (0) | 2022.06.14 |
서로 다른 보존 정책이 주석에 어떤 영향을 미칩니까? (0) | 2022.06.14 |
8비트 문자 이외의 기능이 있는 플랫폼은 어떤 것입니까? (0) | 2022.06.14 |
C에서의 setjmp와 longjmp의 실용적 사용 (0) | 2022.06.13 |