📜  使用子类与超类引用来引用子类对象

📅  最后修改于: 2022-05-13 01:55:52.072000             🧑  作者: Mango

使用子类与超类引用来引用子类对象

先决条件:继承
在Java中,所有非静态方法都基于底层对象的运行时类型,而不是指向该对象的引用类型。因此,无论您在对象声明中使用哪种类型,行为都是相同的。

如何引用子类对象

有两种方法可以引用子类对象。两者都有一些优点/缺点。声明影响在编译时可见的方法上可见。

  1. 第一种方法(使用超类引用进行引用):超类的引用变量可用于引用从该超类派生的任何子类对象。如果方法存在于 SuperClass 中,但被 SubClass 覆盖,则将执行被覆盖的方法。
  2. 第二种方法(使用子类引用进行引用):子类引用可用于引用其对象。

考虑一个解释这两种方法的示例。

// Java program to illustrate 
// referring to a subclass
// base class
class Bicycle 
{
    // the Bicycle class has two fields
    public int gear;
    public int speed;
          
    // the Bicycle class has one constructor
    public Bicycle(int gear, int speed)
    {
        this.gear = gear;
        this.speed = speed;
    }
          
    // the Bicycle class has three methods
    public void applyBrake(int decrement)
    {
        speed -= decrement;
    }
          
    public void speedUp(int increment)
    {
        speed += increment;
    }
      
    // toString() method to print info of Bicycle
    public String toString() 
    {
        return("No of gears are "+gear
                +"\n"
                + "speed of bicycle is "+speed);
    } 
}
  
// derived class
class MountainBike extends Bicycle 
{
      
    // the MountainBike subclass adds one more field
    public int seatHeight;
  
    // the MountainBike subclass has one constructor
    public MountainBike(int gear,int speed,
                        int startHeight)
    {
        // invoking base-class(Bicycle) constructor
        super(gear, speed);
        seatHeight = startHeight;
    } 
          
    // the MountainBike subclass adds one more method
    public void setHeight(int newValue)
    {
        seatHeight = newValue;
    } 
      
    // overriding toString() method
    // of Bicycle to print more info
    @Override
    public String toString() 
    {
          
        return (super.toString()+
                "\nseat height is "+seatHeight);
    }
      
}
  
// driver class
public class Test 
{
    public static void main(String args[]) 
    {
        // using superclass reference
        // first approach
        Bicycle mb2 = new MountainBike(4, 200, 20);
          
        // using subclass reference( )
        // second approach
        MountainBike mb1 = new MountainBike(3, 100, 25);
          
        System.out.println("seat height of first bicycle is " 
                                            + mb1.seatHeight);
              
        // In case of overridden methods
        // always subclass 
        // method will be executed
        System.out.println(mb1.toString());
        System.out.println(mb2.toString());
  
        /* The following statement is invalid because Bicycle
        does not define a seatHeight. 
        // System.out.println("seat height of second bicycle is " 
                                                + mb2.seatHeight); */
                      
        /* The following statement is invalid because Bicycle
        does not define setHeight() method. 
        mb2.setHeight(21);*/
  
    }
}

输出:

seat height of first bicycle is 25
No of gears are 3
speed of bicycle is 100
seat height is 25
No of gears are 4
speed of bicycle is 200
seat height is 20

上述程序说明:

  • MountainBike 类的对象是通过使用子类引用“mb1”来引用的。使用这个引用,我们将可以访问由超类或子类定义的对象的两个部分(方法和变量)。请参阅下图以获得清晰的理解。
    MountainBike mb1 = new MountainBike(3, 100, 25);
    

    z

  • 现在我们再次创建 MountainBike 类的对象,但这次它是通过使用超类 Bicycle 引用“mb2”来引用的。使用这个引用,我们将只能访问由超类定义的对象的那些部分(方法和变量)。
    Bicycle mb2 = new MountainBike(4, 200, 20);
    

    一种

  • 由于引用'mb1'可以访问字段'seatHeight',所以我们在控制台上打印它。
    System.out.println("seat height of first bicycle  is " + mb1.seatHeight);
    
  • 如果超类中存在方法,但被子类覆盖,并且如果创建了子类的对象,那么无论我们使用什么引用(子类或超类),它将始终是子类中被覆盖的方法将被执行。所以下面两条语句会调用 MountainBike 类的 toString() 方法。
    System.out.println(mb1.toString());
    System.out.println(mb2.toString());
    
  • 由于 'mb2' 的引用是 Bicycle 类型,所以我们会在下面的语句中得到编译时错误。
    System.out.println("seat height of second bicycle  is " + mb2.seatHeight);
    
  • 'mb2' 再次引用的类型是 Bicycle ,所以我们将在下面的语句中得到编译时错误。
    mb2.setHeight(21);
    

    类型转换的使用

    在上面的例子中,我们已经看到,通过使用 Bicycle 类型的引用“mb2”,我们无法调用子类特定的方法或访问子类字段。这个问题可以使用Java中的类型转换来解决。例如,我们可以声明另一个引用,比如 MountainBike 类型的“mb3”,并使用类型转换将其分配给“mb2”。

    // declaring MountainBike reference
    MountainBike mb3;
    
    // assigning mb3 to mb2 using typecasting.
     mb3 = (MountainBike)mb2;
    

    所以,现在以下陈述是有效的。

    System.out.println("seat height of second bicycle  is " + mb3.seatHeight);
    mb3.setHeight(21);
    

何时采用第一种方法(使用超类引用进行引用)

如果我们不知道对象的确切运行时类型,那么我们应该使用这种方法。例如,考虑在不同索引处包含不同对象的 ArrayList。现在,当我们尝试使用ArrayList.get(int index)方法获取数组列表的元素时,我们必须使用对象引用,因为在这种情况下,我们不知道对象的确切运行时类型。例如 :

/* Java program to illustrate referring to a subclass
using superclass reference variable */
import java.util.ArrayList;
  
public class Test 
{
       public static void main(String args[]) 
       {
           ArrayList al = new ArrayList(2);
             
           // adding String object to al
           al.add(new String("GeeksForGeeks"));
             
           // adding Integer object to al
           al.add(new Integer(5));
             
           // getting all elements using Object reference
           for (Object object : al)
           {
               System.out.println(object);
           }    
       }
}

输出:

GeeksForGeeks
5

优点:我们可以使用超类引用来保存从它派生的任何子类对象。

缺点:通过使用超类引用,我们将只能访问由超类定义的对象的那些部分(方法和变量)。例如,在上面的第一个示例中,我们不能使用 Bicycle 引用访问seatHeight变量或调用setHeight(int newValue)方法。这是因为它们是在子类中而不是在超类中定义的。

何时采用第二种方法(使用子类引用进行引用)

如果我们知道对象的确切运行时类型,那么这种方法会更好。使用这种方法,我们还可以调用特定对象的特定方法。例如 :

/* Java program to illustrate referring to a subclass
using subclass reference variable */
import java.util.ArrayList;
  
public class Test 
{
       public static void main(String args[]) 
       {
           ArrayList al = new ArrayList(2);
             
           // adding String objects to al
           al.add(new String("GeeksForGeeks"));
           al.add(new String("for java archives"));
             
              
           // getting  elements using String reference
            String str1 = (String)al.get(0);
            String str2 = (String)al.get(1);
              
            System.out.println(str1);
            System.out.println(str2);
              
            // using String class specific method
            System.out.println(str1.length());
            System.out.println(str2.substring(4,8));
       }
}

输出:

GeeksForGeeks
for java archives
13
java

优点:通过使用子类引用,我们将可以访问由超类或子类定义的对象的两个部分(方法和变量)。例如,我们可以使用上面第一个示例中的 MountainBike 引用调用setHeight(int newValue)方法或speedUp(int increment)方法。

缺点:我们可以使用子类引用仅保留特定的子类对象。

参考:堆栈溢出