재정의 된 메소드가 리턴 유형이 다를 수 있습니까?


134

재정의 된 메소드가 다른 리턴 유형을 가질 수 있습니까 ?


2
동일하거나 좁은 리턴 타입이 없다면 ::error: method() in subclass cannot override method() in superclass
Quazi Irfan

답변:


176

Java는 재정의 된 메소드에 대해 * 공변량 리턴 유형을 지원 합니다. 이는 재정의 된 메소드가 보다 구체적인 리턴 유형을 가질 수 있음을 의미합니다 . 즉, 새 리턴 유형이 대체하는 메소드의 리턴 유형에 지정 가능한 한 허용됩니다.

예를 들면 다음과 같습니다.

class ShapeBuilder {
    ...
    public Shape build() {
    ....
}

class CircleBuilder extends ShapeBuilder{
    ...
    @Override
    public Circle build() {
    ....
}

이것은 Java Language Specification의 섹션 8.4.5에 지정되어 있습니다 .

리턴 유형이 참조 유형 인 경우 리턴 유형은 서로 대체하는 메소드마다 다를 수 있습니다. return-type-substitutability의 개념은 공변량 리턴, 즉 리턴 유형을 하위 유형으로 특수화하는 것을 지원합니다.

리턴 유형이 R1 인 메소드 선언 d1은 다음 조건이 충족되는 경우에만 리턴 유형이 R2 인 다른 메소드 d2에 대해 리턴 유형 대체 가능합니다.

  • R1이 void이면 R2가 void입니다.

  • R1이 기본 유형 인 경우 R2는 R1과 동일합니다.

  • R1이 참조 유형 인 경우 :

    • R1은 R2의 하위 유형이거나 R1은 확인되지 않은 변환 (§5.1.9)에 의해 R2의 하위 유형으로 변환 될 수 있습니다. 또는

    • R1 = | R2 |

( "| R2 |"는 JLS의 §4.6에 정의 된대로 R2의 삭제를 나타냅니다 .)


* Java 5 이전에는 Java가 변하지 않았습니다. 리턴 유형을 이는 대체되는 메소드와 정확하게 일치하는 데 필요한 메소드 대체의 리턴 유형을 의미했습니다.


26

예, 다를 수 있지만 몇 가지 제한 사항이 있습니다.

Java 5.0 이전에는 메소드를 대체 할 때 매개 변수와 리턴 유형이 모두 정확히 일치해야합니다. Java 5.0에서는 공변량 리턴 유형이라는 새로운 기능이 도입되었습니다. 동일한 서명으로 메소드를 대체 할 수 있지만 리턴 된 오브젝트의 서브 클래스를 리턴합니다. 즉, 서브 클래스의 메소드는 유형이 수퍼 클래스에서 동일한 서명으로 메소드가 리턴 한 유형의 서브 클래스 인 오브젝트를 리턴 할 수 있습니다.


20

예, 하위 유형을 반환하는 경우 가능합니다. 예를 들면 다음과 같습니다.

package com.sandbox;

public class Sandbox {

    private static class Parent {
        public ParentReturnType run() {
            return new ParentReturnType();
        }
    }

    private static class ParentReturnType {

    }

    private static class Child extends Parent {
        @Override
        public ChildReturnType run() {
            return new ChildReturnType();
        }
    }

    private static class ChildReturnType extends ParentReturnType {
    }
}

이 코드는 컴파일되고 실행됩니다.


9

대체로 예 반환 유형의 재정의 방법이 다를 수 있습니다. 그러나 이와 관련된 경우가 있기 때문에 간단하지 않습니다.

사례 1 : 반환 유형이 기본 데이터 유형이거나 void 인 경우

출력 : 리턴 유형이 void 또는 primitive 인 경우 상위 클래스 메소드 및 대체 메소드의 데이터 유형이 동일해야합니다. 예를 들어 반환 유형이 int, float, string이면 동일해야합니다.

사례 2 : 반환 유형이 파생 된 데이터 유형 인 경우 :

출력 : 상위 클래스 메소드의 리턴 유형이 파생 된 유형 인 경우 대체 메소드의 리턴 유형은 파생 된 데이터 유형과 동일한 서브 클래스의 파생 된 데이터 유형입니다. 예를 들어, 클래스 A가 있고 B가 A의 서브 클래스이고, C가 B의 서브 클래스이고 D가 C의 서브 클래스라고 가정합니다. 슈퍼 클래스가 타입 A를 돌려주는 경우, 우선하는 메소드는 서브 클래스가 A, B, C 또는 D 타입, 즉 그 서브 타입을 돌려 줄 수 있습니다. 이것을 공분산이라고도합니다.


당신이 의견은, B는 A, C 수단의 서브 클래스의 혼란 내가 그것을 클래스 AB는 AC 또는 A의 서브 클래스입니다 무엇을 의미하는지 AB는 AC에 서브 클래스가 BD에 서브 클래스 인 클래스를 가지고 모호
네쉬 kandpal

5

네, 부모 클래스 메소드의 리턴 타입 인 경우에만 .. 반환 유형이 다를 수있다
.. 자식 클래스 메서드의 반환 형식의 슈퍼 유형
수단

class ParentClass {
    public Circle() method1() {
        return new Cirlce();
    }
}

class ChildClass extends ParentClass {
    public Square method1() {
        return new Square();
    }
}

Class Circle {

}

class Square extends Circle {

}


이것이 다른 반환 유형이면 허용 될 수 있습니다 ...


2

대답은 '그렇다'이며 '아니요'입니다.

질문에 달려 있습니다. 여기의 모든 사람들이 Java> = 5에 대해 대답했으며 일부는 Java <5에 공변량 반환 유형이 없습니다.

실제로 Java 언어 사양> = 5는이를 지원하지만 Java 런타임은 지원하지 않습니다. 특히 공변량 리턴 유형을 지원하도록 JVM이 업데이트되지 않았습니다.

당시에는 "영리한"움직임으로 여겨졌지만 Java 역사상 최악의 디자인 결정 중 하나 인 Java 5는 JVM 또는 클래스 파일 사양을 전혀 수정하지 않고 여러 가지 새로운 언어 기능을 구현했습니다. 대신 모든 기능은 javac에서 속임수로 구현되었습니다. 컴파일러는 중첩 / 내부 클래스에 대한 일반 클래스, 제네릭에 대한 유형 삭제 및 캐스트, 중첩 / 내부 클래스 개인 "우정"에 대한 합성 접근 자, 외부 'this'에 대한 합성 인스턴스 필드를 생성 / 사용합니다. 포인터, '.class'리터럴에 대한 합성 정적 필드 등

공변량 리턴 유형은 javac에 의해 더 많은 구문 설탕입니다.

예를 들어, 이것을 컴파일 할 때 :

class Base {
  Object get() { return null; }
}

class Derived extends Base {
  @Override
  @SomeAnnotation
  Integer get() { return null; }
}

javac는 Derived 클래스에서 두 가지 get 메소드를 출력합니다.

Integer Integer:Derived:get() { return null; }
synthetic bridge Object Object:Derived:get() { return Integer:Derived:get(); }

생성 된 브릿지 메소드 (표시 syntheticbridge바이트 코드 로 표시 )는 Object:Base:get()JVM에 대해 리턴 유형이 다른 메소드가 완전히 독립적이며 서로를 대체 할 수 없기 때문에 실제로 대체 됩니다. 예상되는 동작을 제공하기 위해 브리지는 단순히 "실제"메서드를 호출합니다. 위의 예에서 javac는 @SomeAnnotation으로 파생 된 브리지 및 실제 메소드 모두에 주석을 추가합니다.

브릿지 및 실제 메소드는 리턴 유형 만 다르므로 Java 프로그램에서 공존 할 수 없으므로 Java <5에서이 솔루션을 수동으로 코딩 할 수 없습니다. 그러나 JVM 세계에서 메소드 리턴 유형은 메소드 서명의 일부이므로 (인수와 동일) 동일한 이름을 가지며 동일한 인수를 취하는 두 메소드는 리턴 유형이 다르기 때문에 JVM에서 완전히 독립적 인 것으로 간주됩니다. 공존 할 수 있습니다.

(BTW, 필드 유형은 바이트 코드에서 필드 서명의 일부와 유사하므로 다른 유형의 여러 필드를 가지지 만 단일 바이트 코드 클래스 내에서 동일한 이름을 갖는 것이 합법적입니다.)

따라서 귀하의 질문에 완전히 대답하십시오 : JVM은 공변량 리턴 유형을 지원하지 않지만 javac> = 5는 달콤한 구문 설탕 코팅으로 컴파일 타임에 가짜입니다.


1

재정의 및 반환 유형 및 공변량 반환
하위 클래스는 상속 된 버전과 정확히 일치하는 메서드를 정의해야합니다. 또는 Java 5부터는 다음에서 반환 유형을 변경할 수 있습니다.

샘플 코드


                                                                                                            class Alpha {
          Alpha doStuff(char c) {
                  return new Alpha();
              }
           }
             class Beta extends Alpha {
                    Beta doStuff(char c) { // legal override in Java 1.5
                    return new Beta();
                    }
             } } 
Java 5에서이 코드는 컴파일됩니다. 1.4 컴파일러를 사용하여이 코드를 컴파일하려고 시도하면 호환되지 않는 리턴 유형을 사용하려고 시도합니다 – sandeep1987 1 분 전


1

다른 답변은 모두 정확하지만 놀랍게도 여기 이론적 측면을 제외하고는 반환 유형이 다를 수 있지만 Liskov 대체 원칙 때문에 수퍼 클래스에 사용되는 유형 만 제한 할 수 있습니다 .

매우 간단합니다. 메소드를 호출하는 "클라이언트"코드가있는 경우 :

int foo = someBar.bar();

위의 내용이 작동해야합니다 ( int구현 된 구현에 관계없이 무언가를 반환하십시오 bar()).

의미 : 재정의하는 Bar 서브 클래스가있는 경우 bar()"호출자 코드"를 위반하지 않는 것을 리턴해야합니다.

즉, 밑 bar()이 int를 반환 한다고 가정합니다 . 그러면 서브 클래스는 리턴 할 수 short있지만 long호출자가 short값을 잘 처리 할 수 있지만 long!


0

반환 유형은 수퍼 클래스의 원래 재정의 된 메서드에 선언 된 반환 유형과 동일하거나 하위 유형이어야합니다.


5
두 가지 답변을 결합해야합니다.
theblang

0

예 가능합니다

class base {

 base show(){

System.out.println("base class");

return new base();

}
}

class sub extends base{

sub show(){

    System.out.println("sub class");

    return new sub();

 }
}

class inheritance{

 public static void main(String []args) {

        sub obj=new sub();

            obj.show();
 }
}

0

예. 재정의 된 메소드는 다른 리턴 유형을 가질 수 있습니다.

그러나 재정의 된 메소드에는 실제 메소드의 리턴 유형에 대해 더 특정한 유형의 리턴 유형이 있어야한다는 한계가 있습니다.

모든 답변에는 실제 메서드의 반환 유형의 하위 클래스 인 반환 유형을 갖는 재정의 된 메서드의 예가 나와 있습니다.

예를 들면 다음과 같습니다.

public class Foo{

   //method which returns Foo
  Foo getFoo(){
      //your code         
  }

}

 public class subFoo extends Foo{

  //Overridden method which returns subclass of Foo
  @Override
  subFoo getFoo(){
      //your code         
  }

}

인터페이스를 구현하는 클래스조차도 인터페이스의 특정 유형이므로 인터페이스가 예상되는 리턴 유형일 수 있습니다.

예를 들면 다음과 같습니다.

public interface Foo{

   //method which returns Foo
  Foo getFoo();

}

 public class Fizz implements Foo{

  //Overridden method which returns Fizz(as it implements Foo)
  @Override
  Fizz getFoo(){
      //your code         
  }

}

0
class Phone {
    public Phone getMsg() {
        System.out.println("phone...");
        return new Phone();
    }
}

class Samsung extends Phone{
    @Override
    public Samsung getMsg() {
        System.out.println("samsung...");
        return new Samsung();
    }
    
    public static void main(String[] args) {
        Phone p=new Samsung();
        p.getMsg();
    }
}

이 질문에 어떻게 대답합니까?
브랜드가없는 맨체스터

이것은 다른 반환 유형의 예입니다.
Parth
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.