Czy zastąpione metody mogą różnić się typem zwracanego?

141

Czy metody przesłonięte mogą mieć różne typy zwracane ?

santidoo
źródło
2
Jeśli nie masz tego samego lub węższego typu zwrotu, otrzymasz ::error: method() in subclass cannot override method() in superclass
Quazi Irfan

Odpowiedzi:

180

Java obsługuje * kowariantne typy zwracane dla metod przesłoniętych. Oznacza to, że zastąpiona metoda może mieć bardziej szczegółowy typ zwrotu. Oznacza to, że tak długo, jak nowy zwracany typ można przypisać do zwracanego typu metody, którą zastępujesz, jest on dozwolony.

Na przykład:

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

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

Jest to określone w sekcji 8.4.5 specyfikacji języka Java :

Typy zwracane mogą się różnić w zależności od metod, które zastępują się nawzajem, jeśli typy zwracane są typami odwołań. Pojęcie zastępowalności typu zwracanego obsługuje kowariantne zwroty, to znaczy specjalizację typu zwracanego do podtypu.

Deklaracja metody d1 z typem zwrotu R1 jest zastępowalna przez typ zwracany dla innej metody d2 z typem zwracanym R2 wtedy i tylko wtedy, gdy spełnione są następujące warunki:

  • Jeśli R1 jest nieważne, to R2 jest nieważne.

  • Jeśli R1 jest typem pierwotnym, to R2 jest identyczne z R1.

  • Jeśli R1 jest typem odniesienia, to:

    • R1 jest podtypem R2 lub R1 można przekształcić w podtyp R2 przez niesprawdzoną konwersję (§5.1.9), lub

    • R1 = | R2 |

(„| R2 |” odnosi się do usunięcia R2, zgodnie z definicją w §4.6 JLS .)


* Przed wersją Java 5 Java miała niezmienne typy zwracane, co oznaczało zwracany typ przesłonięcia metody niezbędny do dokładnego dopasowania zastępowanej metody.

Laurence Gonsalves
źródło
27

Tak, może się różnić, ale mają pewne ograniczenia.

Przed wersją Java 5.0, gdy zastępujesz metodę, zarówno parametry, jak i zwracany typ muszą dokładnie pasować. W Javie 5.0 wprowadza nową funkcję zwaną kowariantnym typem zwrotu. Możesz przesłonić metodę z tym samym podpisem, ale zwraca podklasę zwróconego obiektu. Innymi słowy, metoda w podklasie może zwrócić obiekt, którego typ jest podklasą typu zwróconego przez metodę z tym samym podpisem w nadklasie.

Pankaj Goyal
źródło
21

Tak, jeśli zwrócą podtyp. Oto przykład:

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

Ten kod kompiluje się i uruchamia.

Daniel Kaplan
źródło
10

Mówiąc ogólnie tak, zwracany typ metody przesłaniania może być inny. Ale nie jest to proste, ponieważ jest kilka przypadków z tym związanych.

Przypadek 1: Jeśli typ zwracany jest pierwotnym typem danych lub void.

Dane wyjściowe: Jeśli zwracany typ jest void lub prymitywny, wówczas typ danych metody klasy nadrzędnej i metody przesłaniającej powinien być taki sam. np. jeśli zwracany typ to int, float, string, to powinien być taki sam

Przypadek 2: Jeśli zwracany typ jest pochodnym typem danych:

Dane wyjściowe: jeśli typ zwracany metody klasy nadrzędnej jest typem pochodnym, typ zwracany metody przesłaniającej jest tym samym typem danych pochodnych podklasy, co typ danych pochodnych. np. załóżmy, że mam klasę A, B jest podklasą A, C jest podklasą B, a D jest podklasą C; wtedy jeśli superklasa zwraca typ A, to metoda przesłaniająca to podklasa może zwrócić typ A, B, C lub D, tj. jego podtypy. Nazywa się to również kowariancją.

Avinav Mishra
źródło
twój ten komentarz jest niejednoznaczny mam klasę AB jest podklasą do AC jest podklasą do BD, co oznacza, że ​​klasa AB jest podklasą AC lub A, B to podklasa A, C Oznacza to mylące
dinesh kandpal
5

tak Jest to możliwe .. typ zwrotów może być inny tylko wtedy, gdy zwracany typ metody klasy nadrzędnej jest
super typem zwracanej metody klasy potomnej ..
oznacza

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

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

Class Circle {

}

class Square extends Circle {

}


Jeśli tak jest, można zezwolić na inny typ zwrotu ...

Achilles Ram Nakirekanti
źródło
2

Pozostałe odpowiedzi są poprawne, ale zaskakująco wszystkie pomijają tutaj aspekt teoretyczny: typy zwracanych mogą być różne, ale mogą ograniczać typ używany w superklasie ze względu na zasadę podstawienia Liskova .

To bardzo proste: gdy masz kod „klienta”, który wywołuje jakąś metodę:

int foo = someBar.bar();

wtedy powyższe musi zadziałać (i zwrócić coś, co jest intbez względu na to, która implementacja bar()zostanie wywołana).

Znaczenie: jeśli istnieje podklasa Bar, która nadpisuje bar(), nadal musisz zwrócić coś, co nie łamie „kodu dzwoniącego”.

Innymi słowy: załóżmy, że baza bar()ma zwrócić int. Wtedy podklasa mogłaby powrócić short- ale nie longdlatego, że wywołujący poradzą sobie z shortwartością, ale nie long!

GhostCat
źródło
2

cóż, odpowiedź brzmi tak ... I NIE.

zależy od pytania. wszyscy tutaj odpowiadali na temat Java> = 5, a niektórzy wspominali, że Java <5 nie zawiera kowariantnych typów zwracanych.

w rzeczywistości specyfikacja języka Java> = 5 obsługuje tę funkcję, ale środowisko wykonawcze Java nie. w szczególności maszyna JVM nie została zaktualizowana w celu obsługi kowariantnych typów zwracanych.

co było wówczas postrzegane jako „sprytne” posunięcie, ale okazało się jedną z najgorszych decyzji projektowych w historii Javy, Java 5 zaimplementowała kilka nowych funkcji językowych bez modyfikowania JVM ani specyfikacji pliku klas. zamiast tego wszystkie funkcje zostały zaimplementowane z oszustwem w javac: kompilator generuje / wykorzystuje zwykłe klasy dla klas zagnieżdżonych / wewnętrznych, wymazywanie typów i rzutowania dla typów generycznych, syntetyczne akcesory dla prywatnej „przyjaźni” klasy zagnieżdżonej / wewnętrznej, syntetyczne pola instancji dla zewnętrznego „this” wskaźniki, syntetyczne pola statyczne dla literałów „.class” itp.

a kowariantne typy zwracane to jeszcze więcej cukru syntaktycznego dodanego przez javac.

na przykład podczas kompilowania tego:

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

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

javac wyświetli dwie metody get w klasie pochodnej:

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

wygenerowany metoda most (oznakowane synthetici bridgew kodu bajtowego) to, co faktycznie nadpisaniaObject:Base:get() ponieważ do JVM, sposoby z różnymi rodzajami obie są całkowicie niezależne i nie mogą zastąpić siebie. aby zapewnić oczekiwane zachowanie, most po prostu wywołuje Twoją „prawdziwą” metodę. w powyższym przykładzie javac doda adnotacje zarówno do metody mostkowej, jak i rzeczywistej w Derived z @SomeAnnotation.

Zwróć uwagę, że nie możesz ręcznie kodować tego rozwiązania w Javie <5, ponieważ metody mostkowe i rzeczywiste różnią się tylko typem zwracanym, a zatem nie mogą współistnieć w programie Java. ale w świecie JVM typy zwracane metod są częścią sygnatury metody (podobnie jak ich argumenty), a więc dwie metody nazwane tak samo i pobierające te same argumenty są mimo wszystko postrzegane jako całkowicie niezależne przez JVM ze względu na ich różne typy zwracania, i mogą współistnieć.

(Przy okazji, typy pól są podobnie częścią podpisu pola w kodzie bajtowym, więc dozwolone jest posiadanie kilku pól różnych typów, ale o takich samych nazwach w ramach jednej klasy kodu bajtowego.)

aby odpowiedzieć na Twoje pytanie w pełni: JVM nie obsługuje kowariantnych typów zwracanych, ale javac> = 5 udaje je w czasie kompilacji, pokrywając je ze słodkiego cukru syntaktycznego.

Lanchon
źródło
1

Przesłanianie i zwracanie typów oraz Covariant Returns
podklasa musi definiować metodę, która dokładnie pasuje do odziedziczonej wersji. Lub, od wersji Java 5, możesz zmienić zwracany typ w

przykładowy kod


                                                                                                            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, ten kod zostanie skompilowany. Jeśli miałbyś spróbować skompilować ten kod za pomocą kompilatora 1.4, powie próba użycia niezgodnego typu powrotu - sandeep1987 1 min temu

sandeep1987
źródło
0

Zwracany typ musi być taki sam, jak typ zwracany zadeklarowany w oryginalnej nadpisanej metodzie w nadklasie lub być jego podtypem.

sandeep1987
źródło
5
Powinieneś połączyć swoje dwie odpowiedzi.
theblang
0

TAK, to możliwe

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
źródło
0

Tak. Możliwe jest, że zastąpione metody mają inny typ zwracania.

Jednak ograniczenia polegają na tym, że zastąpiona metoda musi mieć typ zwracany, który jest bardziej konkretnym typem zwracanego typu rzeczywistej metody.

Wszystkie odpowiedzi podały przykłady zastąpionej metody, która ma typ zwracany, który jest podklasą typu zwracanego rzeczywistej metody.

Na przykład :

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         
  }

}

Ale nie ogranicza się to tylko do podklasy, nawet klasy, które implementują interfejs, są specyficznym typem interfejsu i dlatego mogą być typem zwracanym tam, gdzie interfejs jest oczekiwany.

Na przykład :

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
źródło
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
źródło
Jak to odpowiada na pytanie?
Niemarkowy Manchester
to jest przykład innego typu zwrotu.
Parth