OOP Java:可以将子类返回给父类吗?

时间:2012-02-20 17:10:46

标签: java oop extends

我是OOP的新手,所以我对一个班级extends另一个班级有一个愚蠢的问题。

这是我的例子:

public class Test {
    public Monitor getMonitor(){
        return new LCD();
    }

    class LCD extends Monitor {   NO-ERROR
    class LCD {                   ERROR at line `return new LCD` 
        //some other method or function not in Monitor Class. Example:
        boolean isSamsung;
        public LCD whatkindLCD(){           
        }       
    }
}

我对上面的代码有一个问题:因为LCD是从Monitor扩展而LCD有一些其他属性/方法,而Monitor没有。因此,LCDMonitor的孩子,对吧?

这意味着你试图放一个"大盒子"到一个"小盒子"。那么,为什么当我return new LCD时,Eclipse没有注意到错误,就像我只使用class LCD {一样。

谢谢:)

4 个答案:

答案 0 :(得分:5)

在您的第二个(错误)案例中,您忘记声明LCD确实延伸Monitor。您只是定义了一个“正常”的独立类 - 因此new LCD() 不是 Monitor的实例。

如果您声明LCD类,编译器应该感到高兴:

class LCD extends Monitor {
    //some other method or function not in Monitor Class. Example:
    boolean isSamsung;
    public LCD whatkindLCD(){           
    }       
}

编辑(以回应评论):与LCD相比,Monitor类具有额外的属性/方法根本不是问题。要调用getMonitor()的代码,只关心它返回Monitor - 也就是说,它具有Monitor的所有方法,属性和行为。

因此,如果您的turnOff课程中有displayBitmap(int[][] data)Monitor方法,那么您的LCD课程也会有这些方法。当需要时,LCD的任何实例都可以表现为Monitor - 这是OO语言中子类化的基本原则。因此,无论什么时候需要Monitor,你都可以给它一个LCD,或CRTSamsungLCD的实例,如果有的话,编译器可以满足正确的方法/属性将存在并且可以被调用。

(从技术上讲,如果您喜欢正式定义,这是Liskov substitution principle,但您不需要在详细程度上理解它。)

答案 1 :(得分:3)

方框的比喻是错误的,请改为考虑is a关系。

在第一个例子中,LCD extends Monitor,即LCD is a Monitor,因此在预期显示器的任何地方,LCD都可以。当您在现实世界中考虑而不是查看代码时,您会意识到这是正确的。你可以期待一般显示器所做的一切(例如显示图片),液晶显示器都可以。

在第二个示例中,LCD不是监视器,因此您会收到错误消息。

答案 2 :(得分:1)

将继承理解为“是一种”关系。这是我在新手时期用来理解继承的一个简单例子。

class Employee
{
    String name;
    int salary;

    Employee()
    {
        name = "Employee";
        salary = 5000;
    }
    public String getName()
    {
        return name;
    }
    public int getSalary()
    {
        return salary;
    }
}
class Manager extends Employee
{
    int bonus;
    int salary;

    Manager()
    {
        bonus = 1000;
        salary = 6000;
    }
    public int getBonus()
    {
        return bonus;
    }
    public int getSalary()
    {
        return salary;
    }   
}

class Test
{        
    public static void main(String[] args)
    {
        Employee e = new Employee();
        System.out.println(e.getName());
        //System.out.println(e.getBonus());
        System.out.println(e.getSalary());

        System.out.println();

        Manager m = new Manager();
        System.out.println(m.getName());
        System.out.println(m.getBonus());
        System.out.println(m.getSalary());

        System.out.println();

        Employee em = new Manager();
        System.out.println(em.getName());                   
        //System.out.println(em.getBonus());            
        System.out.println(((Manager)em).getBonus());
        System.out.println(em.getSalary());     
        }
}

编译器在调用任何操作之前查找引用类型。 em.getBonus()不起作用,因为Employee没有奖金方法。 但是使用演员阵容我们可以让它发挥作用。 ((经理)EM)getBonus()

编译器在调用任何操作之前查找引用类型的原因如下:

经理[]经理=新经理[10];

将此数组转换为Employee []数组是合法的:

员工[]员工=经理; //好的

当然,为什么不呢,你可能会想。毕竟,如果经理[i]是经理,那么它也是一名员工。但事实上,令人惊讶的事情正在发生。请记住,经理和员工都是对同一个数组的引用。

现在考虑声明

staff [0] =新员工(“John Eipe”,......);

编译器会愉快地允许这个赋值。 但是员工[0]和经理[0]是相同的参考,所以看起来我们设法将一名员工走私到管理层 行列。

那将是非常糟糕的 - 调用管理器[0] .setBonus(1000)将尝试访问a 不存在的实例字段会破坏相邻的内存。 为了确保不会发生此类损坏,所有数组都会记住元素类型 它们是创建的,它们监视只存储兼容的引用 他们。例如,作为新Manager [10]创建的数组会记住它是一个数组 经理。尝试存储Employee引用会导致ArrayStoreException。

答案 3 :(得分:0)

您的方法getMonitor返回“监视器”类型。

当您使用Monitor扩展类LCD时,您说的是LCD 显示器的编译器。 当您删除 extends 时,LCD类将变为普通类,而不是监视器类型。 如果您将getMonitor修改为

public LCD getMonitor(){
       // code here
}

错误将会消失。