Os métodos substituídos podem diferir no tipo de retorno?

134

Os métodos substituídos podem ter diferentes tipos de retorno ?

santidoo
fonte
2
Se você não tiver o mesmo tipo de retorno ou mais estreito, obterá ::error: method() in subclass cannot override method() in superclass
Quazi Irfan

Respostas:

176

Java suporta * tipos de retorno covariantes para métodos substituídos. Isso significa que um método substituído pode ter um tipo de retorno mais específico. Ou seja, desde que o novo tipo de retorno seja atribuível ao tipo de retorno do método que você está substituindo, é permitido.

Por exemplo:

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

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

Isso é especificado na seção 8.4.5 da Especificação de linguagem Java :

Os tipos de retorno podem variar entre os métodos que se substituem se os tipos de retorno forem tipos de referência. A noção de substituibilidade do tipo de retorno suporta retornos covariantes, ou seja, a especialização do tipo de retorno em um subtipo.

Uma declaração de método d1 com o tipo de retorno R1 é substituível pelo tipo de retorno por outro método d2 com o tipo de retorno R2, se e somente se as seguintes condições forem válidas:

  • Se R1 for nulo, R2 será nulo.

  • Se R1 é um tipo primitivo, R2 é idêntico a R1.

  • Se R1 for um tipo de referência, então:

    • R1 é um subtipo de R2 ou R1 pode ser convertido em um subtipo de R2 por conversão desmarcada (§5.1.9) ou

    • R1 = | R2 |

("| R2 |" refere-se ao apagamento de R2, conforme definido no §4.6 da JLS .)


* Antes do Java 5, o Java tinha tipos de retorno invariantes , o que significava o tipo de retorno de uma substituição de método necessária para corresponder exatamente ao método que estava sendo substituído.

Laurence Gonsalves
fonte
26

Sim, pode ser diferente, mas existem algumas limitações.

Antes do Java 5.0, quando você substitui um método, os parâmetros e o tipo de retorno devem corresponder exatamente. No Java 5.0, ele introduz um novo recurso chamado tipo de retorno covariante. Você pode substituir um método com a mesma assinatura, mas retorna uma subclasse do objeto retornado. Em outras palavras, um método em uma subclasse pode retornar um objeto cujo tipo é uma subclasse do tipo retornado pelo método com a mesma assinatura na superclasse.

Pankaj Goyal
fonte
20

Sim, se eles retornarem um subtipo. Aqui está um exemplo:

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

Esse código compila e executa.

Daniel Kaplan
fonte
9

Em termos gerais, sim, o tipo de retorno do método de substituição pode ser diferente. Mas não é simples, pois há alguns casos envolvidos nisso.

Caso 1: se o tipo de retorno for um tipo de dados primitivo ou nulo.

Saída: se o tipo de retorno for nulo ou primitivo, o tipo de dados do método da classe pai e o método de substituição deve ser o mesmo. por exemplo, se o tipo de retorno for int, float, string, deve ser o mesmo

Caso 2: se o tipo de retorno for derivado, digite:

Saída: se o tipo de retorno do método da classe pai for do tipo derivado, o tipo de retorno do método de substituição será o mesmo tipo de subclasse de dados derivado do tipo de dados derivado. por exemplo, suponha que eu possua uma classe A, B é uma subclasse para A, C é uma subclasse para B e D é uma subclasse para C; então, se a superclasse estiver retornando o tipo A, o método substituto da subclasse poderá retornar os tipos A, B, C ou D, ou seja, seus subtipos. Isso também é chamado de covariância.

Avinav Mishra
fonte
seu comentário é ambíguo eu tenho uma classe AB é subclasse para AC é subclasse para BD, o que significa Classe AB é subclasse de AC ou A, B são subclasse de A, C Significa sua confusão
dinesh kandpal
5

sim É possível .. o tipo de retorno pode ser diferente apenas se o tipo de retorno do método da classe pai for
um super tipo de tipo de retorno do método da classe filho ..
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 {

}


Se esse for o tipo de retorno diferente, poderá ser permitido ...

Achilles Ram Nakirekanti
fonte
2

Bom, a resposta é sim ou não.

depende da pergunta. todos aqui responderam sobre Java> = 5, e alguns mencionaram que Java <5 não apresenta tipos de retorno covariantes.

na verdade, a especificação da linguagem Java> = 5 suporta, mas o Java Runtime não. em particular, a JVM não foi atualizada para suportar tipos de retorno covariantes.

no que foi visto na época como uma jogada "inteligente", mas acabou sendo uma das piores decisões de design da história do Java, o Java 5 implementou um monte de novos recursos de linguagem sem modificar a JVM ou a especificação do arquivo de classe. em vez disso, todos os recursos foram implementados com truques no javac: o compilador gera / usa classes simples para classes aninhadas / internas, tipo apagamento e conversão para genéricos, acessores sintéticos para "amizade" privada / classe interna aninhada / interna, campos de instância sintética para 'this' externo ponteiros, campos estáticos sintéticos para literais '.class', etc., etc.

e tipos de retorno covariante são ainda mais açúcar sintático adicionado por javac.

por exemplo, ao compilar isso:

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

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

O javac produzirá dois métodos get na classe Derived:

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

o método de ponte gerado (marcado synthetic e bridgeem bytecode) é o que realmente substitui Object:Base:get()porque, para a JVM, métodos com diferentes tipos de retorno são completamente independentes e não podem substituir um ao outro. para fornecer o comportamento esperado, a ponte simplesmente chama seu método "real". no exemplo acima, o javac anotará os métodos bridge e real em Derived with @SomeAnnotation.

observe que você não pode codificar manualmente esta solução em Java <5, porque os métodos bridge e reais diferem apenas no tipo de retorno e, portanto, não podem coexistir em um programa Java. mas no mundo da JVM, os tipos de retorno de método fazem parte da assinatura do método (assim como seus argumentos) e, portanto, os dois métodos com o mesmo nome e usando os mesmos argumentos são vistos como completamente independentes pela JVM devido a seus diferentes tipos de retorno, e pode coexistir.

(BTW, os tipos de campos também fazem parte da assinatura do campo no bytecode, portanto, é legal ter vários campos de tipos diferentes, mas com o mesmo nome em uma única classe de bytecode.)

para responder sua pergunta completamente: a JVM não suporta tipos de retorno covariantes, mas javac> = 5 a falsifica no tempo de compilação com uma camada de açúcar sintático doce.

Lanchon
fonte
1

Substituindo e retornando tipos e retornos covariantes,
a subclasse deve definir um método que corresponda exatamente à versão herdada. Ou, a partir do Java 5, você pode alterar o tipo de retorno no

Código de amostra


                                                                                                            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, esse código será compilado. Se você tentar compilar esse código com um compilador 1.4, dirá que tentar usar o tipo de retorno incompatível - sandeep1987 1 minuto atrás

sandeep1987
fonte
1

As outras respostas estão todas corretas, mas surpreendentemente, deixando de fora o aspecto teórico aqui: os tipos de retorno podem ser diferentes, mas só podem restringir o tipo usado na superclasse por causa do Princípio de Substituição de Liskov .

É super simples: quando você tem um código "cliente" que chama algum método:

int foo = someBar.bar();

o que foi dito acima deve funcionar (e retornar algo que intnão importa qual implementação bar()seja invocada).

Significado: se houver uma subclasse Bar que substitua bar(), você ainda precisará retornar algo que não quebra o "código do chamador".

Em outras palavras: suponha que a base bar()deva retornar int. Em seguida, uma subclasse pode retornar short- mas não longporque os chamadores estarão bem em lidar com um shortvalor, mas não a long!

GhostCat
fonte
0

O tipo de retorno deve ser o mesmo ou um subtipo do tipo de retorno declarado no método original substituído na superclasse.

sandeep1987
fonte
5
Você deve combinar suas duas respostas.
theblang
0

SIM, pode ser possível

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

Sim. É possível que métodos substituídos tenham tipos de retorno diferentes.

Mas as limitações são que o método substituído deve ter um tipo de retorno que seja o tipo mais específico do tipo de retorno do método real.

Todas as respostas deram exemplos do método substituído para ter um tipo de retorno que é uma subclasse do tipo de retorno do método real.

Por exemplo :

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         
  }

}

Mas isso não se limita apenas à subclasse. Mesmo as classes que implementam uma interface são um tipo específico da interface e, portanto, podem ser um tipo de retorno onde a interface é esperada.

Por exemplo :

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
fonte
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
fonte
Como isso responde à pergunta?
Manchester sem marca
Este é o exemplo de um tipo de retorno diferente.
Parth 14/07