วิธีการแทนที่อาจแตกต่างกันในประเภทผลตอบแทนหรือไม่


134

วิธีการแทนที่สามารถมีผลตอบแทนประเภทต่างๆได้หรือไม่


2
หากคุณไม่มีผลตอบแทนประเภทเดียวกันหรือแคบกว่าคุณจะได้รับ ::error: method() in subclass cannot override method() in superclass
Quazi Irfan

คำตอบ:


176

Java สนับสนุน*ประเภทผลตอบแทน covariant สำหรับวิธีการแทนที่ ซึ่งหมายความว่าเมธอดที่ถูกลบล้างอาจมีประเภทผลตอบแทนที่เฉพาะเจาะจงมากขึ้น นั่นคือตราบใดที่ประเภทการส่งคืนใหม่สามารถกำหนดให้กับประเภทการส่งคืนของวิธีการที่คุณกำลังลบล้างได้ก็จะได้รับอนุญาต

ตัวอย่างเช่น:

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: หากประเภทการส่งคืนเป็นชนิดข้อมูลดั้งเดิมหรือเป็นโมฆะ

ผลลัพธ์: ถ้าประเภทการส่งคืนเป็นโมฆะหรือดั้งเดิมชนิดข้อมูลของเมธอดคลาสพาเรนต์และเมธอดการแทนที่ควรจะเหมือนกัน เช่นถ้า return type เป็น 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

ใช่เป็นไปได้ .. ประเภทการส่งคืนอาจแตกต่างกันได้ก็ต่อเมื่อประเภทการส่งคืนเมธอดของคลาสพาเรนต์เป็น
ประเภทการส่งคืนเมธอดคลาสย่อยของ super type ..
หมายถึง

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 หรือข้อมูลจำเพาะของ classfile เลย แทนที่จะใช้คุณสมบัติทั้งหมดด้วยเล่ห์เหลี่ยมใน javac: คอมไพเลอร์สร้าง / ใช้คลาสธรรมดาสำหรับคลาสที่ซ้อนกัน / ชั้นในพิมพ์ลบและแคสต์สำหรับ generics ตัวเข้าถึงสังเคราะห์สำหรับ "มิตรภาพ" คลาสส่วนตัวแบบซ้อน / อินสแตนซ์ฟิลด์อินสแตนซ์สังเคราะห์สำหรับด้านนอก 'this' พอยน์เตอร์ฟิลด์สแตติกสังเคราะห์สำหรับลิเทอรัล ".class" ฯลฯ ฯลฯ

และประเภทผลตอบแทนที่เป็นโควาเรียยังเป็นน้ำตาลเชิงไวยากรณ์ที่เพิ่มโดย javac

ตัวอย่างเช่นเมื่อรวบรวมสิ่งนี้:

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

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

javac จะแสดงผลสองวิธีรับในคลาส Derived:

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

เมธอดบริดจ์ที่สร้างขึ้น (ทำเครื่องหมายsyntheticและbridgeอยู่ในรหัส bytecode) คือสิ่งที่แทนที่จริงObject:Base:get()เนื่องจากสำหรับ JVM เมธอดที่มีประเภทการส่งคืนที่แตกต่างกันนั้นเป็นอิสระอย่างสมบูรณ์และไม่สามารถแทนที่กัน เพื่อให้เกิดพฤติกรรมที่คาดหวังสะพานเพียงเรียกวิธีการ "จริง" ของคุณ ในตัวอย่างด้านบน javac จะใส่คำอธิบายประกอบทั้งบริดจ์และวิธีการจริงใน Derived with @SomeAnnotation

โปรดทราบว่าคุณไม่สามารถเขียนโค้ดด้วยมือโซลูชันนี้ใน Java <5 ได้เนื่องจากบริดจ์และวิธีการจริงแตกต่างกันในประเภทการส่งคืนเท่านั้นจึงไม่สามารถอยู่ร่วมกันในโปรแกรม Java ได้ แต่ในโลก JVM ประเภทการส่งคืนเมธอดเป็นส่วนหนึ่งของลายเซ็นของวิธีการ (เช่นเดียวกับอาร์กิวเมนต์) ดังนั้นทั้งสองวิธีที่ตั้งชื่อเหมือนกันและรับอาร์กิวเมนต์เดียวกันจึงถูกมองว่าเป็นอิสระอย่างสมบูรณ์โดย JVM เนื่องจากประเภทผลตอบแทนที่แตกต่างกัน และสามารถอยู่ร่วมกันได้

(BTW ประเภทของฟิลด์เป็นส่วนหนึ่งของลายเซ็นฟิลด์ในไบต์โค้ดในทำนองเดียวกันดังนั้นจึงเป็นเรื่องถูกกฎหมายที่จะมีฟิลด์หลายประเภทที่แตกต่างกัน แต่ตั้งชื่อเหมือนกันภายในคลาส bytecode เดียว)

ดังนั้นเพื่อตอบคำถามของคุณอย่างเต็มที่: JVM ไม่รองรับประเภทการส่งคืนโควาเรียร์ แต่ javac> = 5 จะปลอมในเวลารวบรวมด้วยการเคลือบน้ำตาลวากยสัมพันธ์แบบหวาน


1

การแทนที่และประเภทการส่งคืนและ Covariant ส่งคืน
คลาสย่อยต้องกำหนดวิธีการที่ตรงกับเวอร์ชันที่สืบทอดมาทุกประการ หรือใน 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 นาทีที่ผ่านมา


1

คำตอบอื่น ๆ นั้นถูกต้องทั้งหมด แต่น่าแปลกใจที่ทุกคนทิ้งแง่มุมทางทฤษฎีไว้ที่นี่ประเภทผลตอบแทนอาจแตกต่างกัน แต่สามารถจำกัด ได้เท่านั้นชนิดที่ใช้ในชั้นซุปเปอร์เพราะLiskov ชดเชยหลักการ

ง่ายมาก: เมื่อคุณมีรหัส "ไคลเอนต์" ที่เรียกวิธีการบางอย่าง:

int foo = someBar.bar();

จากนั้นข้างต้นจะต้องทำงาน (และส่งคืนสิ่งที่เป็นไฟล์ intไม่ว่าbar()จะเรียกใช้งานใดก็ตาม)

ความหมาย: หากมีคลาสย่อยของ Bar ที่แทนที่bar()คุณจะต้องส่งคืนสิ่งที่ไม่ทำลาย "รหัสผู้โทร"

กล่าวอีกนัยหนึ่ง: สมมติว่าฐานbar()ควรจะคืนค่า int จากนั้นคลาสย่อยสามารถส่งคืนshort- แต่ไม่ใช่longเพราะผู้โทรจะจัดการกับshortค่าได้ดี แต่ไม่ใช่long!


0

ประเภทการส่งคืนต้องเหมือนกับหรือประเภทย่อยของประเภทการส่งคืนที่ประกาศในเมธอดเดิมที่ถูกแทนที่ในซูเปอร์คลาส


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

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

สิ่งนี้ตอบคำถามอย่างไร
Unbranded Manchester

นี่คือตัวอย่างของผลตอบแทนประเภทต่างๆ
Parth
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.