คำตอบ:
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 มีประเภทการส่งคืนที่ไม่แน่นอนซึ่งหมายถึงประเภทการส่งคืนของการแทนที่เมธอดที่จำเป็นเพื่อให้ตรงกับเมธอดที่ถูกแทนที่ทุกประการ
ใช่มันอาจแตกต่างกัน แต่มีข้อ จำกัด บางประการ
ก่อน Java 5.0 เมื่อคุณแทนที่เมธอดทั้งพารามิเตอร์และชนิดการส่งคืนจะต้องตรงกันทุกประการ ใน Java 5.0 แนะนำสิ่งอำนวยความสะดวกใหม่ที่เรียกว่าประเภทการส่งคืนโควาเรียน คุณสามารถแทนที่เมธอดที่มีลายเซ็นเดียวกัน แต่ส่งคืนคลาสย่อยของอ็อบเจ็กต์ที่ส่งคืน กล่าวอีกนัยหนึ่งเมธอดในคลาสย่อยสามารถส่งคืนอ็อบเจ็กต์ที่มีประเภทเป็นคลาสย่อยของประเภทที่ส่งคืนโดยเมธอดที่มีลายเซ็นเดียวกันในซูเปอร์คลาส
ใช่ถ้าพวกเขาส่งคืนประเภทย่อย นี่คือตัวอย่าง:
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 {
}
}
รหัสนี้รวบรวมและรัน
การพูดอย่างกว้าง ๆ ว่าประเภทผลตอบแทนของวิธีการแทนที่อาจแตกต่างกัน แต่มันไม่ตรงไปตรงมาเนื่องจากมีบางกรณีที่เกี่ยวข้องกับเรื่องนี้
กรณีที่ 1: หากประเภทการส่งคืนเป็นชนิดข้อมูลดั้งเดิมหรือเป็นโมฆะ
ผลลัพธ์: ถ้าประเภทการส่งคืนเป็นโมฆะหรือดั้งเดิมชนิดข้อมูลของเมธอดคลาสพาเรนต์และเมธอดการแทนที่ควรจะเหมือนกัน เช่นถ้า return type เป็น int, float, string ก็ควรจะเหมือนกัน
กรณีที่ 2: หากประเภทการส่งคืนเป็นชนิดข้อมูลที่ได้รับ:
เอาต์พุต: ถ้าประเภทการส่งคืนของเมธอดคลาสพาเรนต์เป็นชนิดที่ได้รับมาประเภทการส่งคืนของเมธอดการแทนที่จะเป็นชนิดข้อมูลที่ได้รับมาเดียวกันของคลาสย่อยกับชนิดข้อมูลที่ได้รับ เช่นสมมติว่าฉันมีคลาส A, B เป็นคลาสย่อยของ A, C คือคลาสย่อยของ B และ D คือคลาสย่อยของ C; จากนั้นถ้าซุปเปอร์คลาสส่งคืนประเภท A ดังนั้นวิธีการแทนที่คือคลาสย่อยสามารถส่งคืนประเภท A, B, C หรือ D คือประเภทย่อย สิ่งนี้เรียกอีกอย่างว่าความแปรปรวนร่วม
ใช่เป็นไปได้ .. ประเภทการส่งคืนอาจแตกต่างกันได้ก็ต่อเมื่อประเภทการส่งคืนเมธอดของคลาสพาเรนต์เป็น
ประเภทการส่งคืนเมธอดคลาสย่อยของ 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 {
}
คำตอบคือใช่ ... และไม่ใช่
ขึ้นอยู่กับคำถาม ทุกคนที่นี่ตอบเกี่ยวกับ 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 จะปลอมในเวลารวบรวมด้วยการเคลือบน้ำตาลวากยสัมพันธ์แบบหวาน
การแทนที่และประเภทการส่งคืนและ 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 นาทีที่ผ่านมา
คำตอบอื่น ๆ นั้นถูกต้องทั้งหมด แต่น่าแปลกใจที่ทุกคนทิ้งแง่มุมทางทฤษฎีไว้ที่นี่ประเภทผลตอบแทนอาจแตกต่างกัน แต่สามารถจำกัด ได้เท่านั้นชนิดที่ใช้ในชั้นซุปเปอร์เพราะLiskov ชดเชยหลักการ
ง่ายมาก: เมื่อคุณมีรหัส "ไคลเอนต์" ที่เรียกวิธีการบางอย่าง:
int foo = someBar.bar();
จากนั้นข้างต้นจะต้องทำงาน (และส่งคืนสิ่งที่เป็นไฟล์ int
ไม่ว่าbar()
จะเรียกใช้งานใดก็ตาม)
ความหมาย: หากมีคลาสย่อยของ Bar ที่แทนที่bar()
คุณจะต้องส่งคืนสิ่งที่ไม่ทำลาย "รหัสผู้โทร"
กล่าวอีกนัยหนึ่ง: สมมติว่าฐานbar()
ควรจะคืนค่า int จากนั้นคลาสย่อยสามารถส่งคืนshort
- แต่ไม่ใช่long
เพราะผู้โทรจะจัดการกับshort
ค่าได้ดี แต่ไม่ใช่long
!
ประเภทการส่งคืนต้องเหมือนกับหรือประเภทย่อยของประเภทการส่งคืนที่ประกาศในเมธอดเดิมที่ถูกแทนที่ในซูเปอร์คลาส
ใช่มันเป็นไปได้
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();
}
}
ใช่. เป็นไปได้ที่เมธอดที่ถูกแทนที่จะมีประเภทการส่งคืนที่แตกต่างกัน
แต่ข้อ จำกัด คือเมธอดที่ถูกแทนที่จะต้องมีชนิดการส่งคืนที่เฉพาะเจาะจงมากขึ้นของประเภทการส่งคืนของเมธอดจริง
คำตอบทั้งหมดได้ยกตัวอย่างของเมธอดที่ถูกแทนที่เพื่อให้มีประเภทการส่งคืนซึ่งเป็นคลาสย่อยของประเภทการส่งคืนของวิธีการจริง
ตัวอย่างเช่น :
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
}
}
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();
}
}
error: method() in subclass cannot override method() in superclass