Können sich überschriebene Methoden im Rückgabetyp unterscheiden?

134

Können überschriebene Methoden unterschiedliche Rückgabetypen haben ?

Santidoo
quelle
2
Wenn Sie nicht den gleichen oder engeren Rückgabetyp haben, erhalten Sie ::error: method() in subclass cannot override method() in superclass
Quazi Irfan

Antworten:

176

Java unterstützt * kovariante Rückgabetypen für überschriebene Methoden. Dies bedeutet eine überschriebene Methode einen hat mehr spezifischen Rückgabetyp. Das heißt, solange der neue Rückgabetyp dem Rückgabetyp der Methode zugeordnet ist, die Sie überschreiben, ist dies zulässig.

Beispielsweise:

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

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

Dies ist in Abschnitt 8.4.5 der Java-Sprachspezifikation angegeben :

Rückgabetypen können zwischen Methoden variieren, die sich gegenseitig überschreiben, wenn die Rückgabetypen Referenztypen sind. Der Begriff der Substituierbarkeit des Rückgabetyps unterstützt kovariante Rückgaben, dh die Spezialisierung des Rückgabetyps auf einen Subtyp.

Eine Methodendeklaration d1 mit dem Rückgabetyp R1 kann genau dann durch eine andere Methode d2 mit dem Rückgabetyp R2 ersetzt werden, wenn die folgenden Bedingungen erfüllt sind:

  • Wenn R1 nichtig ist, ist R2 nichtig.

  • Wenn R1 ein primitiver Typ ist, ist R2 identisch mit R1.

  • Wenn R1 ein Referenztyp ist, dann:

    • R1 ist entweder ein Subtyp von R2 oder R1 kann durch ungeprüfte Konvertierung (§5.1.9) in einen Subtyp von R2 konvertiert werden, oder

    • R1 = | R2 |

("| R2 |" bezieht sich auf das Löschen von R2 gemäß Definition in §4.6 des JLS .)


* Vor Java 5 hatte Java invariante Rückgabetypen, was bedeutete, dass der Rückgabetyp einer Methodenüberschreibung erforderlich war, um genau mit der überschriebenen Methode übereinzustimmen.

Laurence Gonsalves
quelle
26

Ja, es kann abweichen, aber es gibt einige Einschränkungen.

Vor Java 5.0 müssen beim Überschreiben einer Methode sowohl die Parameter als auch der Rückgabetyp genau übereinstimmen. In Java 5.0 wird eine neue Funktion namens kovarianter Rückgabetyp eingeführt. Sie können eine Methode mit derselben Signatur überschreiben, aber eine Unterklasse des zurückgegebenen Objekts zurückgeben. Mit anderen Worten, eine Methode in einer Unterklasse kann ein Objekt zurückgeben, dessen Typ eine Unterklasse des Typs ist, der von der Methode mit derselben Signatur in der Oberklasse zurückgegeben wird.

Pankaj Goyal
quelle
20

Ja, wenn sie einen Subtyp zurückgeben. Hier ist ein Beispiel:

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 {
    }
}

Dieser Code wird kompiliert und ausgeführt.

Daniel Kaplan
quelle
8

Im Großen und Ganzen kann die Art der überschreibenden Methode "Ja" unterschiedlich sein. Aber es ist nicht einfach, da es einige Fälle gibt, die damit zusammenhängen.

Fall 1: Wenn der Rückgabetyp ein primitiver Datentyp oder eine Leere ist.

Ausgabe: Wenn der Rückgabetyp ungültig oder primitiv ist, sollten der Datentyp der übergeordneten Klassenmethode und der überschreibenden Methode identisch sein. Wenn der Rückgabetyp beispielsweise int, float, string ist, sollte er identisch sein

Fall 2: Wenn der Rückgabetyp vom Datentyp abgeleitet ist:

Ausgabe: Wenn der Rückgabetyp der übergeordneten Klassenmethode vom abgeleiteten Datentyp ist, ist der Rückgabetyp der überschreibenden Methode der gleiche abgeleitete Datentyp der Unterklasse wie der abgeleitete Datentyp. Angenommen, ich habe eine Klasse A, B ist eine Unterklasse von A, C ist eine Unterklasse von B und D ist eine Unterklasse von C; Wenn dann die Superklasse Typ A zurückgibt, kann die überschreibende Methode Unterklasse A, B, C oder D zurückgeben, dh ihre Untertypen. Dies wird auch als Kovarianz bezeichnet.

Avinav Mishra
quelle
Ihr dieser Kommentar ist mehrdeutig Ich habe eine Klasse AB ist Unterklasse zu AC ist Unterklasse zu BD, was es bedeutet Klasse AB ist Unterklasse von AC oder A, B sind Unterklasse von A, C
Bedeutet
5

Ja Es ist möglich, dass der Rückgabetyp nur dann unterschiedlich sein kann, wenn der Rückgabetyp der übergeordneten Klassenmethode
ein Supertyp des Rückgabetyps der untergeordneten Klassenmethode
ist

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

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

Class Circle {

}

class Square extends Circle {

}


Wenn dies der andere Rückgabetyp ist, kann er zugelassen werden ...

Achilles Ram Nakirekanti
quelle
2

Nun, die Antwort ist ja ... und nein.

hängt von der Frage ab. Alle hier antworteten in Bezug auf Java> = 5, und einige erwähnten, dass Java <5 keine kovarianten Rückgabetypen enthält.

Die Java-Sprachspezifikation> = 5 unterstützt dies tatsächlich, die Java-Laufzeit jedoch nicht. Insbesondere wurde die JVM nicht aktualisiert, um kovariante Rückgabetypen zu unterstützen.

Java 5 implementierte eine Reihe neuer Sprachfunktionen, ohne die JVM oder die Klassendateispezifikation zu ändern. Stattdessen wurden alle Funktionen mit Tricks in Javac implementiert: Der Compiler generiert / verwendet einfache Klassen für verschachtelte / innere Klassen, Typlöschung und Casts für Generika, synthetische Accessoren für verschachtelte / innere Klassen, private "Freundschaft", synthetische Instanzfelder für äußere "dies". Zeiger, synthetische statische Felder für '.class'-Literale usw. usw.

und kovariante Rückgabetypen sind noch mehr syntaktischer Zucker, der von Javac hinzugefügt wird.

Zum Beispiel beim Kompilieren:

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

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

javac gibt zwei get-Methoden in der Derived-Klasse aus:

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

Die generierte Bridge-Methode (markiert syntheticund bridgein Bytecode) wird tatsächlich überschrieben, Object:Base:get()da für die JVM Methoden mit unterschiedlichen Rückgabetypen völlig unabhängig sind und sich nicht gegenseitig überschreiben können. Um das erwartete Verhalten zu erzielen, ruft die Bridge einfach Ihre "echte" Methode auf. Im obigen Beispiel kommentiert javac sowohl Bridge- als auch Real-Methoden in Derived mit @SomeAnnotation.

Beachten Sie, dass Sie diese Lösung in Java <5 nicht manuell codieren können, da sich Bridge- und Real-Methoden nur im Rückgabetyp unterscheiden und daher in einem Java-Programm nicht koexistieren können. In der JVM-Welt sind Methodenrückgabetypen (genau wie ihre Argumente) Teil der Methodensignatur. Daher werden die beiden Methoden mit demselben Namen und denselben Argumenten von der JVM aufgrund ihrer unterschiedlichen Rückgabetypen als völlig unabhängig angesehen. und kann koexistieren.

(Übrigens sind die Feldtypen in ähnlicher Weise Teil der Feldsignatur im Bytecode, daher ist es legal, mehrere Felder unterschiedlichen Typs zu haben, die jedoch innerhalb einer einzelnen Bytecode-Klasse gleich benannt sind.)

Um Ihre Frage vollständig zu beantworten: Die JVM unterstützt keine kovarianten Rückgabetypen, aber javac> = 5 fälscht sie zur Kompilierungszeit mit einer Beschichtung aus süßem syntaktischem Zucker.

Lanchon
quelle
1

Überschreiben und Zurückgeben von Typen sowie kovariante Rückgaben
Die Unterklasse muss eine Methode definieren, die genau mit der geerbten Version übereinstimmt. Ab Java 5 können Sie den Rückgabetyp in der ändern

Beispielcode


                                                                                                            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 wird dieser Code kompiliert. Wenn Sie versuchen, diesen Code mit einem 1.4-Compiler zu kompilieren, wird der Versuch angezeigt, einen inkompatiblen Rückgabetyp zu verwenden - sandeep1987 vor 1 Minute

Sandeep1987
quelle
1

Die anderen Antworten sind alle richtig, aber überraschenderweise lassen sie hier alle den theoretischen Aspekt aus: Rückgabetypen können unterschiedlich sein, aber sie können den in der Superklasse verwendeten Typ aufgrund des Liskov-Substitutionsprinzips nur einschränken .

Es ist ganz einfach: Wenn Sie einen "Client" -Code haben, der eine Methode aufruft:

int foo = someBar.bar();

dann muss das oben genannte funktionieren (und etwas zurückgeben, das intunabhängig von der Implementierung istbar() ist, aufgerufen wird).

Bedeutung: Wenn es eine Balkenunterklasse gibt, die überschreibt bar() Sie immer noch etwas zurückgeben, das den "Anrufercode" nicht beschädigt.

Mit anderen Worten: Nehmen Sie an, dass die Basis bar()int zurückgeben soll. Dann könnte eine Unterklasse zurückkehren short- aber nicht, longweil Anrufer mit einem shortWert gut umgehen können, aber nicht mit einem long!

GhostCat
quelle
0

Der Rückgabetyp muss mit dem Rückgabetyp oder einem Untertyp des Rückgabetyps identisch sein, der in der ursprünglich überschriebenen Methode in der Oberklasse deklariert wurde.

Sandeep1987
quelle
5
Sie sollten Ihre beiden Antworten kombinieren.
Theblang
0

JA, es kann möglich sein

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();
 }
}
simplePerson43
quelle
0

Ja. Es ist möglich, dass überschriebene Methoden unterschiedliche Rückgabetypen haben.

Die Einschränkungen bestehen jedoch darin, dass die überschriebene Methode einen Rückgabetyp haben muss, der spezifischer vom Rückgabetyp der tatsächlichen Methode ist.

Alle Antworten enthalten Beispiele für die überschriebene Methode mit einem Rückgabetyp, der eine Unterklasse des Rückgabetyps der tatsächlichen Methode ist.

Zum Beispiel :

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         
  }

}

Dies ist jedoch nicht nur auf die Unterklasse beschränkt. Selbst Klassen, die eine Schnittstelle implementieren, sind ein bestimmter Typ der Schnittstelle und können daher ein Rückgabetyp sein, bei dem die Schnittstelle erwartet wird.

Zum Beispiel :

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         
  }

}
Sachin Kumar
quelle
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
quelle
Wie beantwortet dies die Frage?
Manchester ohne
Dies ist das Beispiel eines anderen Rückgabetyps.
Parth