拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Java方法的签名是否包含返回类型?

Java方法的签名是否包含返回类型?

白鹭 - 2021-11-24 667 0 0

1.概述

方法签名只是Java中整个方法定义的子集。因此,签名的确切解剖结构可能引起混乱。

在本教程中,我们将学习方法签名的元素及其在Java编程中的含义。

2.方法签名

Java中的方法支持重载,这意味着可以在相同的类或类层次结构中定义多个具有相同名称的方法。因此,编译器必须能够静态绑定客户端代码所引用的方法。因此,方法签名唯一地标识每个方法

根据Oracle的说法,方法签名由名称和参数类型组成。因此,方法声明的所有其他元素(例如修饰符,返回类型,参数名称,异常列表和主体)都不是签名的一部分。

让我们仔细研究方法重载及其与方法签名的关系。

3.重载错误

让我们考虑以下代码:

public void print() {

 System.out.println("Signature is: print()");

 }



 public void print(int parameter) {

 System.out.println("Signature is: print(int)");

 }

如我们所见,由于方法具有不同的参数类型列表,因此代码会编译。实际上,编译器可以确定性地将任何调用绑定到一个或另一个。

现在,通过添加以下方法来测试重载是否合法:

public int print() {

 System.out.println("Signature is: print()");

 return 0;

 }

编译时,出现“类中已定义方法”错误。证明方法返回类型不是方法签名的一部分

让我们尝试使用修饰符:

private final void print() {

 System.out.println("Signature is: print()");

 }

我们仍然看到相同的“方法已经在类中定义”错误。因此,方法签名不依赖于修饰符

通过更改抛出的异常来重载可以通过添加以下内容进行测试:

public void print() throws IllegalStateException {

 System.out.println("Signature is: print()");

 throw new IllegalStateException();

 }

再次,我们看到“方法已经在类中定义”错误,表明throw声明不能成为签名的一部分

我们可以测试的最后一件事是更改参数名称是否允许重载。让我们添加以下方法:

public void print(int anotherParameter) {

 System.out.println("Signature is: print(int)");

 }

如预期的那样,我们得到相同的编译错误。这意味着参数名称不会影响方法签名

3.泛型和类型擦除

使用通用参数,类型擦除会更改有效签名。实际上,它可能与使用通用类型上限而不是通用令牌的另一种方法发生冲突。

让我们考虑以下代码:

public class OverloadingErrors<T extends Serializable> {



 public void printElement(T t) {

 System.out.println("Signature is: printElement(T)");

 }



 public void printElement(Serializable o) {

 System.out.println("Signature is: printElement(Serializable)");

 }

 }

即使签名看起来不同,类型删除后,编译器也无法静态绑定正确的方法。

由于类型擦除Serializable,我们可以看到编译器将T替换为上限Serializable。因此,它与使用Serializable显式地冲突。

当泛型类型没有边界时,我们将在基本类型Object

4.参数列表和多态

方法签名考虑了确切的类型。这意味着我们可以重载其参数类型为子类或超类的方法。

但是,我们必须特别注意,因为静态绑定将尝试使用多态性,自动装箱和类型提升进行匹配

让我们看下面的代码:

public Number sum(Integer term1, Integer term2) {

 System.out.println("Adding integers");

 return term1 + term2;

 }



 public Number sum(Number term1, Number term2) {

 System.out.println("Adding numbers");

 return term1.doubleValue() + term2.doubleValue();

 }



 public Number sum(Object term1, Object term2) {

 System.out.println("Adding objects");

 return term1.hashCode() + term2.hashCode();

 }

上面的代码完全合法,可以编译。调用这些方法时可能会引起混乱,因为我们不仅需要知道所调用的确切方法签名,而且还需要Java如何根据实际值静态绑定。

让我们探索一些最终绑定到sum(Integer, Integer)方法调用:

StaticBinding obj = new StaticBinding();

 obj.sum(Integer.valueOf(2), Integer.valueOf(3));

 obj.sum(2, 3);

 obj.sum(2, 0x1);

对于第一个调用,我们具有确切的参数类型Integer, Integer.在第二次调用中,Java将为我们自动将int装箱为Integer .最后,Java将0x1转换为int ,然后将其自动装箱为Integer.

同样,我们有以下绑定到sum(Number, Number)调用:

obj.sum(2.0d, 3.0d);

 obj.sum(Float.valueOf(2), Float.valueOf(3));

在第一个电话中,我们有double值,这些值会自动装箱为Double.然后,通过多态性, Double精度匹配Number.同样, Float与第二个呼叫的Number

让我们观察一个事实,即FloatDouble都继承自NumberObject.但是,默认绑定是Number 。这是由于Java将自动匹配与方法签名匹配的最接近的超类型。

现在让我们考虑以下方法调用:

obj.sum(2, "John");

在此示例中,我们为第一个参数设置了intInteger但是,此方法名称sum(Integer, String)因此,Java将遍历所有参数超类型,以从最接近的父代强制转换为Object直到找到匹配项为止。在这种情况下,它绑定到sum(Object, Object).

要更改默认绑定,我们可以使用显式参数强制转换,如下所示:

obj.sum((Object) 2, (Object) 3);

 obj.sum((Number) 2, (Number) 3);

5. Vararg参数

现在,我们将注意力转移到**varargs如何影响方法的有效签名**和静态绑定。

这里我们有一个使用varargs的重载方法:

public Number sum(Object term1, Object term2) {

 System.out.println("Adding objects");

 return term1.hashCode() + term2.hashCode();

 }



 public Number sum(Object term1, Object... term2) {

 System.out.println("Adding variable arguments: " + term2.length);

 int result = term1.hashCode();

 for (Object o : term2) {

 result += o.hashCode();

 }

 return result;

 }

那么这些方法的有效签名是什么?我们已经看到sum(Object, Object)是第一个的签名。变量参数本质上是数组,因此编译后第二个变量的有效签名是sum(Object, Object[]).

一个棘手的问题是,只有两个参数时,如何选择方法绑定?

让我们考虑以下调用:

obj.sum(new Object(), new Object());

 obj.sum(new Object(), new Object(), new Object());

 obj.sum(new Object(), new Object[]{new Object()});

显然,第一个调用将绑定到sum(Object, Object) ,第二个sum(Object, Object[]).要强制Java使用两个对象来调用第二个方法,我们必须像在第三个调用中一样将其包装在数组中。

这里要注意的最后一件事是,声明以下方法将与vararg版本冲突:

public Number sum(Object term1, Object[] term2) {

 // ...

 }

六,结论

在本教程中,我们了解到方法签名由名称和参数类型的列表组成。修饰符,返回类型,参数名称和异常列表无法区分重载方法,因此也不是签名的一部分。

我们还研究了类型擦除和varargs如何隐藏有效的方法签名,以及如何覆盖Java的静态方法绑定。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *