¿Pueden los métodos anulados diferir en el tipo de retorno?

134

¿Pueden los métodos anulados tener diferentes tipos de retorno ?

santidoo
fuente
2
Si no tiene el mismo tipo de retorno o uno más estrecho, obtendrá ::error: method() in subclass cannot override method() in superclass
Quazi Irfan

Respuestas:

176

Java admite * tipos de retorno covariantes para métodos anulados. Esto significa que un método anulado puede tener más tipo de retorno específico. Es decir, siempre que el nuevo tipo de retorno sea asignable al tipo de retorno del método que está anulando, está permitido.

Por ejemplo:

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

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

Esto se especifica en la sección 8.4.5 de la Especificación del lenguaje Java :

Los tipos de retorno pueden variar entre los métodos que se anulan entre sí si los tipos de retorno son tipos de referencia. La noción de sustituibilidad de tipo de retorno admite retornos covariantes, es decir, la especialización del tipo de retorno a un subtipo.

Una declaración de método d1 con tipo de retorno R1 es sustituible por tipo de retorno por otro método d2 con tipo de retorno R2, si y solo si se cumplen las siguientes condiciones:

  • Si R1 es nulo, entonces R2 es nulo.

  • Si R1 es un tipo primitivo, R2 es idéntico a R1.

  • Si R1 es un tipo de referencia, entonces:

    • R1 es un subtipo de R2 o R1 se puede convertir en un subtipo de R2 mediante conversión sin marcar (§5.1.9), o

    • R1 = | R2 |

("| R2 |" se refiere a la eliminación de R2, como se define en §4.6 del JLS ).


* Antes de Java 5, Java tenía tipos de retorno invariables , lo que significaba que el tipo de retorno de una anulación de método necesitaba coincidir exactamente con el método que se anulaba.

Laurence Gonsalves
fuente
26

Sí, puede diferir, pero hay algunas limitaciones.

Antes de Java 5.0, cuando anula un método, ambos parámetros y el tipo de retorno deben coincidir exactamente. En Java 5.0, introduce una nueva instalación llamada tipo de retorno covariante. Puede anular un método con la misma firma pero devuelve una subclase del objeto devuelto. En otras palabras, un método en una subclase puede devolver un objeto cuyo tipo es una subclase del tipo devuelto por el método con la misma firma en la superclase.

Pankaj Goyal
fuente
20

Sí, si devuelven un subtipo. Aquí hay un ejemplo:

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

Este código se compila y se ejecuta.

Daniel Kaplan
fuente
9

En términos generales, sí, el tipo de retorno del método de anulación puede ser diferente. Pero no es sencillo, ya que hay algunos casos involucrados en esto.

Caso 1: si el tipo de retorno es un tipo de datos primitivo o nulo.

Salida: si el tipo de retorno es nulo o primitivo, el tipo de datos del método de clase principal y el método de anulación deberían ser los mismos. por ejemplo, si el tipo de retorno es int, float, string, entonces debería ser el mismo

Caso 2: si el tipo de retorno es un tipo de datos derivado:

Salida: si el tipo de retorno del método de la clase principal es el tipo derivado, entonces el tipo de retorno del método de anulación es el mismo tipo de datos derivados de la subclase al tipo de datos derivados. por ejemplo, supongamos que tengo una clase A, B es una subclase de A, C es una subclase de B y D es una subclase de C; entonces, si la superclase devuelve el tipo A, entonces el método de anulación es la subclase puede devolver el tipo A, B, C o D, es decir, sus subtipos. Esto también se llama covarianza.

Avinav Mishra
fuente
tu este comentario es ambiguo tengo una clase AB es una subclase de AC es una subclase de BD, lo que significa que la clase AB es una subclase de AC o A, B es una subclase de A, C significa confuso
dinesh kandpal
5

sí Es posible ... el tipo de retorno puede ser diferente solo si el tipo de retorno del método de la clase primaria es
un supertipo del tipo de retorno del método de la clase secundaria ...
significa

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

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

Class Circle {

}

class Square extends Circle {

}


Si este es el tipo de retorno diferente, se puede permitir ...

Achilles Ram Nakirekanti
fuente
2

Bueno, la respuesta es si o no.

Depende de la pregunta. todos aquí respondieron con respecto a Java> = 5, y algunos mencionaron que Java <5 no presenta tipos de retorno covariantes.

en realidad, la especificación del lenguaje Java> = 5 lo admite, pero el tiempo de ejecución de Java no. en particular, la JVM no se actualizó para admitir tipos de retorno covariantes.

En lo que se vio entonces como un movimiento "inteligente", pero terminó siendo una de las peores decisiones de diseño en la historia de Java, Java 5 implementó un montón de nuevas características de lenguaje sin modificar la JVM o la especificación del archivo de clase. en su lugar, todas las características se implementaron con trucos en javac: el compilador genera / usa clases simples para clases anidadas / internas, borrado de tipos y moldes para genéricos, accesores sintéticos para "amistad" privada anidada / interna, campos de instancia sintéticos para "esto" externo punteros, campos estáticos sintéticos para literales '.class', etc., etc.

y los tipos de retorno covariante son aún más azúcar sintáctico agregado por javac.

por ejemplo, al compilar esto:

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

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

javac generará dos métodos get en la clase Derivada:

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

El método puente generado (marcado syntheticy bridgeen código de bytes) es lo que realmente se anula Object:Base:get()porque, para la JVM, los métodos con diferentes tipos de retorno son completamente independientes y no pueden anularse entre sí. Para proporcionar el comportamiento esperado, el puente simplemente llama a su método "real". en el ejemplo anterior, javac anotará métodos puente y reales en Derivado con @SomeAnnotation.

tenga en cuenta que no puede codificar manualmente esta solución en Java <5, porque los métodos bridge y real solo difieren en el tipo de retorno y, por lo tanto, no pueden coexistir en un programa Java. pero en el mundo de JVM, los tipos de retorno de métodos son parte de la firma del método (al igual que sus argumentos) y, por lo tanto, los dos métodos nombrados igual y tomando los mismos argumentos son considerados completamente independientes por la JVM debido a sus diferentes tipos de retorno, y puede coexistir

(Por cierto, los tipos de campos son igualmente parte de la firma del campo en bytecode, por lo que es legal tener varios campos de diferentes tipos pero con el mismo nombre dentro de una sola clase de bytecode).

para responder completamente a su pregunta: la JVM no admite tipos de retorno covariantes, pero javac> = 5 lo simula en tiempo de compilación con una capa de azúcar sintáctico dulce.

Lanchon
fuente
1

Anulación y tipos de retorno, y retornos covariantes
La subclase debe definir un método que coincida exactamente con la versión heredada. O, a partir de Java 5, puede cambiar el tipo de retorno en

Código de muestra


                                                                                                            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, este código se compilará. Si intentara compilar este código con un compilador 1.4 dirá que intenta usar un tipo de retorno incompatible - sandeep1987 hace 1 minuto

sandeep1987
fuente
1

Las otras respuestas son todas correctas, pero sorprendentemente todas omiten el aspecto teórico aquí: los tipos de retorno pueden ser diferentes, pero solo pueden restringir el tipo utilizado en la superclase debido al Principio de sustitución de Liskov .

Es súper simple: cuando tienes un código de "cliente" que llama a algún método:

int foo = someBar.bar();

entonces lo anterior tiene que funcionar (y devolver algo que es un intno importa qué implementación bar()se invoque).

Significado: si hay una subclase de Bar que anula, bar()entonces todavía tiene que devolver algo que no rompa el "código de llamada".

En otras palabras: suponga que se supone que la base bar()debe devolver int. Entonces, una subclase podría regresar short, pero no longporque las personas que llamen estarán bien tratando con un shortvalor, ¡pero no con un long!

GhostCat
fuente
0

El tipo de retorno debe ser el mismo o un subtipo del tipo de retorno declarado en el método anulado original en la superclase.

sandeep1987
fuente
55
Debes combinar tus dos respuestas.
theblang
0

SÍ, puede ser posible

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
fuente
0

Si. Es posible que los métodos anulados tengan un tipo de retorno diferente.

Pero las limitaciones son que el método reemplazado debe tener un tipo de retorno que sea un tipo más específico del tipo de retorno del método real.

Todas las respuestas han dado ejemplos del método reemplazado para tener un tipo de retorno que es una subclase del tipo de retorno del método real.

Por ejemplo :

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         
  }

}

Pero esto no solo se limita a la subclase. Incluso las clases que implementan una interfaz son un tipo específico de la interfaz y, por lo tanto, pueden ser un tipo de retorno donde se espera que la interfaz.

Por ejemplo :

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
fuente
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
fuente
¿Cómo responde esto a la pregunta?
Manchester sin marca
Este es el ejemplo de un tipo de retorno diferente.
Part