Могут ли переопределенные методы различаться по типу возвращаемого значения?

134

Могут ли переопределенные методы иметь разные возвращаемые типы ?

santidoo
источник
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() {
    ....
}

Это указано в разделе 8.4.5 Спецификации языка Java :

Типы возвращаемых данных могут различаться в зависимости от методов, которые переопределяют друг друга, если типы возвращаемых значений являются ссылочными. Понятие заменяемости возвращаемого типа поддерживает ковариантные возвраты, то есть специализацию возвращаемого типа по подтипу.

Объявление метода d1 с типом возвращаемого значения R1 может быть заменено типом возвращаемого значения другим методом d2 с типом возвращаемого значения R2, ​​если и только если выполняются следующие условия:

  • Если R1 недействителен, то R2 недействителен.

  • Если R1 - примитивный тип, то R2 идентичен R1.

  • Если R1 - ссылочный тип, тогда:

    • R1 является либо подтипом R2, либо R1 может быть преобразован в подтип R2 путем неконтролируемого преобразования (§5.1.9), или

    • R1 = | R2 |

(«| R2 |» относится к стиранию R2, как определено в §4.6 JLS .)


* До 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: Если возвращаемый тип является примитивным типом данных или пустым.

Вывод: если тип возвращаемого значения недействителен или примитивен, тогда тип данных метода родительского класса и метода переопределения должен быть одинаковым. например, если тип возвращаемого значения - int, float, string, тогда он должен быть таким же

Случай 2: Если возвращаемый тип является производным типом данных:

Выходные данные: если тип возвращаемого значения метода родительского класса является производным типом, тогда тип возвращаемого значения метода переопределения является тем же производным типом данных подкласса, что и производный тип данных. например, предположим, что у меня есть класс A, B - подкласс A, C - подкласс B, а D - подкласс C; тогда, если суперкласс возвращает тип A, тогда метод переопределения подкласса может возвращать тип A, B, C или D, то есть его подтипы. Это также называется ковариацией.

Авинав Мишра
источник
ваш этот комментарий неоднозначен, у меня есть класс AB, подкласс AC, подкласс BD, что это означает, что класс AB является подклассом AC или A, B является подклассом A, C означает, что это сбивает с толку
dinesh 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 выведет два метода get в классе Derived:

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

сгенерированный метод моста (помеченный syntheticи bridgeв байт-коде) - это то, что фактически переопределяет, Object:Base:get()потому что для JVM методы с разными типами возврата полностью независимы и не могут переопределять друг друга. чтобы обеспечить ожидаемое поведение, мост просто вызывает ваш «настоящий» метод. в приведенном выше примере javac будет аннотировать как мостовые, так и реальные методы в Derived с помощью @SomeAnnotation.

обратите внимание, что вы не можете вручную запрограммировать это решение на Java <5, потому что мост и реальные методы различаются только типом возвращаемого значения и, следовательно, они не могут сосуществовать в программе Java. но в мире JVM возвращаемые типы методов являются частью сигнатуры метода (точно так же, как их аргументы), и поэтому два метода, названные одинаково и принимающие одни и те же аргументы, тем не менее рассматриваются JVM как полностью независимые из-за их различных возвращаемых типов, и могут сосуществовать.

(Кстати, типы полей аналогичным образом являются частью подписи поля в байт-коде, поэтому допустимо иметь несколько полей разных типов, но с одинаковыми именами в одном классе байт-кода.)

поэтому, чтобы полностью ответить на ваш вопрос: JVM не поддерживает ковариантные типы возврата, но javac> = 5 имитирует его во время компиляции с покрытием сладкого синтаксического сахара.

Lanchon
источник
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 минуту назад

sandeep1987
источник
1

Все остальные ответы верны, но, что удивительно, здесь не учитывается теоретический аспект: типы возвращаемых данных могут быть разными, но они могут ограничивать только тип, используемый в суперклассе, из-за принципа подстановки Лискова .

Это очень просто: когда у вас есть «клиентский» код, который вызывает какой-то метод:

int foo = someBar.bar();

тогда вышеуказанное должно работать (и возвращать что-то, intнезависимо от того, какая реализация была bar()вызвана).

Значение: если есть подкласс Bar, который переопределяет, bar()вам все равно нужно вернуть что-то, что не нарушает «код вызывающего абонента».

Другими словами: предположим, что база bar()должна возвращать int. Тогда подкласс может вернуться short- но не longпотому, что вызывающие будут иметь дело со shortзначением, но не с long!

GhostCat
источник
0

Тип возвращаемого значения должен быть таким же, как тип возвращаемого значения, объявленным в исходном переопределенном методе в суперклассе, или являться его подтипом.

sandeep1987
источник
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();
 }
}
simplePerson43
источник
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
источник
Как это отвечает на вопрос?
Манчестер других производителей,
это пример другого возвращаемого типа.
Parth