Java中动态和静态类型赋值之间的区别

时间:2013-12-10 20:37:59

标签: java inheritance dynamic static

给定以下类层次结构,以下语句的动态和静态类型是什么?

类层次结构:

class Alpha {}

class Beta extends Alpha {}

class Gamma extends Alpha {}

class Epsilon extends Alpha{}

class Fruit extends Gamma{}

class Golf extends Beta {}

class Orange extends Fruit{}

对于以下每个语句,静态类型?动态类型?:

Fruit f = new Fruit();
Alpha a = f;
Beta b = f;
a = b;
Gamma g = f;

我的回答/问题
我知道Fruit f = new Fruit()将是静态和动态类型的Fruit。
Alpha a = f;在编译时将为Alpha类型(静态),并在运行时键入Fruit(动态)。
Gamma g = f;在编译时将是Gamma类型(静态)并在运行时键入Fruit(动态)。
但是我不知道另外两个答案。 Beta b = f是一个实例,其中同一个超类的两个子类彼此分配,所以我不确定它在编译时是类型Beta还是类型Alpha(静态)。并且a = b是声明后的赋值,所以我不确定答案是什么。有人请帮帮我谢谢!

5 个答案:

答案 0 :(得分:13)

我正在匆匆打字,所以请原谅任何错别字(我稍后会在有机会时解决这些错误)。

  

我知道Fruit f = new Fruit()将是静态和动态类型的Fruit。

我认为您将static and dynamic类型的术语与编译时和运行时类型相混淆(或者当您将类型A的对象的地址分配给类型B的指针时,在C ++中也是如此B是A的父类。)

禁止反射技巧,Java中没有动态类型。在编译时,所有内容都是静态类型的。运行时对象的类型与编译它的对象类型相同。

发生的事情是您将对象引用(a,b,c,f)与堆中实例化的实际对象混淆(使用new创建的任何对象。)

在Java中,f是对象引用,而不是对象本身。此外,f的引用类型为Fruit and sub-classes of it。您分配给它的对象(new Fruit())恰好是Fruit类型。

现在,您的示例代码中的所有其他引用都是reference to A and sub-classes of it类型; b的类型为reference to B and sub-classes of it;等等。

请记住这一点,因为它非常重要。

  

Alpha a = f;在编译时将是Alpha类型(静态)并在运行时键入Fruit(动态)。

a类型为'对类型A和子类的引用'。 f的类型是'类型为Fruit和子类的引用'。

f指向的对象是'Fruit'类型。当你说'a = f'时,你没有将'f'分配给'a'。你说'现在将引用f当前引用的东西'。

那么在那个作业之后,什么是a引用?在赋值时指向的对象引用Fruit类型f的对象。

记住,a,b,g,f,它们不是对象。它们是使用new运算符以这种或那种方式创建的对象的引用或句柄。

a,b或f等引用变量与使用new创建的对象不同。但事实恰恰相反,前者可以指向后者。

在运行时使用new创建的对象的类型与在编译时确定的对象的类型相同。

  

伽玛g = f;在编译时将是Gamma类型(静态)并在运行时键入Fruit(动态)。

与上述相同。变量g是类型reference to type Gamma and sub-classes的对象引用。在此分配中,g将指向f指向的同一对象。那个对象的类型是什么?在编译时给出相同的结果:Fruit。

  

但我不知道另外两个答案。 Beta b = f是一个实例   其中同一超类的两个子类被分配给一个   另一个所以我不确定它是Beta型还是Alpha型   编译时间(静态)。

b的类型为reference to type Beta and sub-classes of it。赋值b = f后指向的对象的类型为Fruit,它是编译时的类型。

  1. 在编译时确定对象a,b,g和f的类型。它们是静态类型的,不会在运行时更改。

  2. 使用new创建的对象的类型也在编译时确定。它们也是静态类型的,不会在运行时更改。

  3. 对象,stuff对象引用a,b,g和f指向在运行时,这取决于编译器是否发现语句有效。分配可以更改,但这与对象引用或对象本身是静态还是动态类型无关。

  4. 如果您希望在动态和静态类型之间找到明确的区别,请考虑以下事项:

    // Java, statically typed.
    int x = 3;
    x = 5; // good
    x = "hi"; // compiler error
    
    ## Ruby, dynamically typed
    x = 3 # ok
    x = 5 # ok
    x = "hi" # still ok
    

    然后强类型和弱/鸭类型语言之间存在区别(两者都可以动态输入。)有很多关于这个主题的文献。

    希望它有所帮助。

答案 1 :(得分:3)

  

以下语句的动态和静态类型

嗯,语句没有类型,至少Java语言规范中没有这样的概念。规范确实定义了两种不同的类型:变量,字段或参数的声明类型,以及对象的运行时类

如名称所示,变量,字段或参数的声明类型是您在声明中提到的类型。例如,声明Foo bar;声明了一个名为bar的{​​{1}}类型的变量。

对象的运行时类由用于构造它的类实例或数组创建表达式确定,并且在该对象的整个生命周期内保持不变。

所以代码:

Foo

分别声明3个Integer i = 1; Number n = i; Object o = n; IntegerNumber类型的变量,所有这些变量都引用具有运行时类Object的单个对象。

答案 2 :(得分:2)

f的具体运行时类型是Fruit(正如您在问题中所说的那样)。

因此Beta b = f;初始化一个变量,其声明的编译时类型为Beta,其运行时类型为Fruit。这不会编译,因为f的编译时类型是Fruit,而Fruit不是Beta的子类,因此f不能分配给Beta类型的变量。 / p>

a = b;b中,其运行时类型为Fruit(参见上文)被分配给变量a,声明为Alpha a。所以a的编译时类型是Alpha,其运行时类型是Fruit。

答案 3 :(得分:2)

首先澄清"参考变量"类型:

Object obj;

指向任何内容,参考变量obj将没有类型。 现在

Object obj = new String();
System.out.println(obj.getClass());//prints class java.lang.String

obj指向一个String,而引用变量obj指向String类型。

关键是Java是一种静态类型语言,所有引用类型变量都有一个在编译时分配的类型。引用变量obj可以指向其他对象,只要它是Object的子类即可。在这种情况下 几乎所有的东西。考虑

Object obj = new String();
System.out.println(obj.getClass());//prints class java.lang.String
Number num = new Byte((byte)9);
obj = num;
System.out.println(obj.getClass());//prints class java.lang.Byte

在运行时,与编译时相同,引用变量obj的类型为Byte。

对于我来说,对象的静态/动态类型与继承有关。 更具体地说,是压倒一切的机制。也称为动态多态 和后期绑定。

考虑覆盖Object类中的equals():

public class Types {

    @Override
    public boolean equals(Object obj){
        System.out.println("in class Types equals()");
        return false;//Shut-up compiler!
    }

    public static void main(String[] args){
        Object typ = new Types();
        typ.equals("Hi");//can do this as String is a subclass of Object
    }
}

现在我们知道引用变量typ的类型是Types。

Object typ = new Types();

说到

typ.equals("Hi");

这就是我认为编译器的想法。

如果equals()是

1.NOT静态和最终,它是。

2.从基类引用(很快就会有更多内容)。

然后编译器推迟调用JVM的方法。调用的确切方法取决于调用该方法的变量的动态类型(更快)。在我们的例子中,参考变量是典型的。 这称为动态方法调用。

现在从基类引用: 从上面的代码

Object typ = new Types();
typ.equals("Hi");

Type Object可以被视为typ的基本类型,也称为引用变量的 Static Type ,而equals()是从基类型引用的,在本例中是Object。 / p>

如果我们有

Types typ = new Types();

没有基类型的引用,因此没有动态方法调用。

现在指向参考变量的动态类型

Object typ = new Types();
typ.equals("Hi");

typ的动态类型是类型,根据动态方法调用,类类型中的equals()将在运行时调用。

还可以说我们有另一个扩展Types,TypesSubClass的类。 而TypesSubClass也有一个重写的equals()。然后

Object typ = new TypesSubClass();
typ.equals("Hi");

将使Typ TypesSubClass和TypesSubClass'的动态类型 将在运行时调用equals()。

说实话,我个人并不知道为什么我们需要所有这些并且已经发布了一个有关此问题的问题。检查

What is the reason behind Dynamic Method Resolution in a staticlly typed language like Java

答案 4 :(得分:0)

您应该看一下这篇文章:http://www.sitepoint.com/typing-versus-dynamic-typing/

静态类型是指语言不需要初始化变量。

例如。

/* C code */ 
static int num, sum; // explicit declaration 
num = 5; // now use the variables 
sum = 10; 
sum = sum + num;

动态类型是语言需要初始化变量的时候。

例如

/* Python code */ 
num = 10 // directly using the variable