首先来看一段代码
我们把这段代码称为代码①,接下来我们再来看另外一段代码
我们把这段代码称为代码②。
在代码①当中,定义了一个方法,这个方法声明的返回值类型是double,而实际通过return关键字返回的却是一个int型的值,但并没有引起编译错误。而在代码②当中,情况正好相反,方法声明的返回值类型是int,方法内部的return关键字实际返回了一个double类型的值,在这种情况下,编译器却报出了语法错误。
对比一下这两种情况,很多初学Java的小伙伴搞不清:为什么同样是方法声明的返回值类型与实际返回值的类型不相同,第一种情况不报错,而另一种情况却无法通过编译检查呢?要讲清楚这个问题,我们必须从为什么方法要声明返回值类型说起。
在Java语言中,要求程序员在定义一个方法的时候,必须在方法名称的前面声明这个方法的返回值类型。为什么要求你这么做呢?简单来说,就是编译器强制程序员必须公开一个信息,那就是:这个方法在运行之后会返回一个什么类型的值,只有这样,调用方法的人才知道要用一个什么类型的变量接收方法的运算结果。除此之外,编译器还要求:主调方法在接收返回值的过程中,必须以声明的返回值类型作为准,而不是看实际返回值的类型。就拿代码①来说,声明的返回值类型是double,实际返回值是int,我们在程序中用变量接收方法返回值的时候,必须用double型变量接收,即便是它实际返回的只是一个int型数据,也不能用int型变量接收返回值,否则就会出现编译错误
接下来,我们再来观察方法的定义过程。在代码①当中,方法所声明的返回值类型是double型,而实际在方法的内部返回的是一个int型的值,二者之间类型并不一致,但并没有出现语法错误,这又是怎么回事呢?这是因为,Java语言在做语法检查的时候,要求只要实际的返回值类型能够”自动转换”为方法所声明的返回值类型即可,并不要求二者必须完全相同。代码①当中的方法,实际返回值是int类型的,它可以自动转换为一个double类型的值。而代码②当中的方法,实际返回值是double类型的,无法自动转换成int类型,所以报错了。专业上,我们把编译器可以自动完成的类型转换也称之为“隐式类型转换”。
为了说明为什么方法声明的返回值为double类型,但实际却可以在方法中返回一个int型的数据,我们来举个容易理解的例子:你小的时候,你爸爸要出门,告诉你在他回来的时候会带回一个直径50厘米的大蛋糕,那么你肯定会按照他的承诺,准备一个直径50厘米的大盘子用来装这个大蛋糕。但你爸爸回来的时候,实际带回来的却是一个直径只有10厘米的小蛋糕,这时虽然你很伤心也很失望,但是,你所准备的大盘子足够能放得下你爸爸带回来的小蛋糕,不会出什么问题。代码①当中定义的方法就属于这种情况,声明返回一个占8字节的double型数据,但实际返回一个占4字节的int型数据。
但是反过来,你爸爸出门的时候告诉你他回来的时候会带回一个10厘米的小蛋糕,你也按照他的承诺准备了一个直径10厘米的小盘子,打算用来装这个小蛋糕。但当你爸爸回来的时候,带回的却是一个直径达50厘米的大蛋糕!这时,你又惊喜又激动,但是却无法解决一个问题:你所准备的小盘子根本无法盛放这么大的蛋糕!这就好比是代码②中所展现的情况,声明返回一个只占4字节的int型数据,而方法运行实际返回一个8字节的double型数据,让那些准备用int型变量接收方法返回值的人感到“措手不及”。当然,这只是一句玩笑,如果真的出现了实际返回值无法自动转换成声明的类型,编译器根本不允许这样的代码通过编译,所以也更谈不上运行代码了。
接下来再具体说说哪些情况下,编译器能够自动把一种类型”自动转换”成另一种类型,也就是实现我们所说的”隐式类型转换”。我们可以分以下几种情况讨论:
一、我们知道,Java基础数据类型有8种,其中整数类型有4中,分别是long、int、short和byte。这4种整数类型有着明显的“向下兼容”的特性,也就是说,较小的数据类型都可以自动转换为较大的数据类型,比如int类型的数据在赋值给long类型的变量时,可以自动完成从int到long的转换。因此,声明返回值为较大的数据类型,实际返回较小的数据类型,是肯定没有问题。比如,声明返回值为long类型,但实际返回int型数据是完全可以的。在这种情况下,int型的数据返回到主调方法中,在赋值给一个long类型的变量时,可以自动完成类型转换。同样,两种浮点数double和float也能够做到“向下兼容”,声明double型,返回float型也完全可以。
二、我们还知道,表示字符的char类型,在实际存储数据的过程中,存储的也是一个占两个字节的”整数”,这个”整数”其实就是字符的编码值。那么char类型的数据能否与Java的4种整型完成自动类型转换呢?实际情况是:char可以自动转换为long和int,但不能自动转换为short和byte。反过来,short和byte也不能自动转换为char。说的直白一点,就是如果我们把方法的返回值声明为char类型,方法实际返回的是long、int、short和byte型的数据都不行。
三、任意整型和字符型都可以自动转换为浮点型,但反之不成立。也就是说, long、int、short、byte以及char都可以自动转换为double和float。但反过来,double和float都无法自动转换成long、int、short、byte以及char。这里可能有写小伙伴会有一点疑惑:8个字节的long类型数据真的能自动转换为4个字节的float型吗?从语法的角度是没有问题的,我们把一个long类型的数据赋值给一个float类型的变量不需要做强制类型转换,编译器也不会报错。但实际存储过程中,有可能会导致数据损失精度。注意,这里所说的是“有可能”损失精度,而不是”一定”损失精度。
四、boolean类型在8种基础数据类型中是”特立独行”的,它无法与任何其他基础类型的数据完成相互自动转换的操作,如果一个方法声明返回值类型为boolean,那么这个方法只能返回boolean类型的数据,返回任何其他基础数据类型的值都会报错。
以上我们谈到的方法返回值都是基础数据类型,如果是引用数据类型,情况又如何呢?这种情况下,遵循“子类对象可以自动转为父类对象”的原则。也就是说,如果返回值被声明为父类,而实际返回对象为子类对象,完全可以顺利通过编译器语法检查。同样,如果返回值类型被声明为接口,方法实际返回对象为接口的实现类对象,也没有问题。而反过来,方法声明返回值类型为子类,实际返回值为父类对象是无法通过编译检查的。
希望本文对初学Java的小伙伴理解方法的返回值有所帮助。
如想系统学习Java编程,可以点击这里观看我在本站的视频课程,有问题也可以加入我的QQ群291839907一起讨论。