方法重载并选择最具体的类型

时间:2012-02-20 13:06:56

标签: java static-methods overloading

示例代码为:

    public class OverloadingTest {

       public static void test(Object obj){
           System.out.println("Object called");
       }

       public static void test(String obj){
           System.out.println("String called");
       }

       public static void main(String[] args){
           test(null);
           System.out.println("10%2==0 is "+(10%2==0));
           test((10%2==0)?null:new Object());
           test((10%2==0)?null:null);
   }

输出是:

  

字符串称为
  10%2 == 0为真   对象叫做
  字符串称为

test(null)的第一次调用使用String参数调用该方法,根据The Java Language Specification可以理解。

1)任何人都可以解释我在前面的调用中调用test()的基础吗?

2)当我们提出时,再说if条件:

    if(10%2==0){
        test(null);
    }
    else
    {
        test(new Object());
    }

它始终使用String参数调用方法。

编译时编译器会计算表达式(10%2)吗?我想知道表达式是在编译时还是在运行时计算的。感谢。

9 个答案:

答案 0 :(得分:20)

Java使用早期绑定。在编译时选择最具体的方法。通过参数数量和参数类型选择最具体的方法。在这种情况下,参数数量无关紧要。这给我们留下了参数类型。

参数有哪些类型?两个参数都是表达式,使用三元条件运算符。问题简化为:条件三元运算符返回什么类型?类型在编译时计算。

给出两个表达式:

(10%2==0)? null : new Object(); // A
(10%2==0)? null : null; // B

列出了here类型评估规则。在B中很容易,两个术语完全相同:null将被返回(whatever type that may be)(JLS:"如果第二个和第三个操作数具有相同的类型(这可能是null类型),那么这就是条件表达式的类型。")。在A中,第二个词来自特定的类。由于这是更具体的,null可以替换类Object的对象,整个表达式的类型是Object(JLS:"如果是第二个和第三个中的一个操作数是null类型而另一个的类型是引用类型,那么条件表达式的类型就是引用类型。")。

在表达式的类型评估之后,方法选择符合预期。

您提供的if示例不同:您使用两个不同类型的对象调用方法。三元条件运算符始终在编译时中被评估为一个类型,适合这两个术语。

答案 1 :(得分:2)

test((10%2==0)?null:new Object());

与:

相同
Object o;

if(10%2==0)
    o=null;
else
    o=new Object();

test(o);

由于o的类型为Object(就像(10%2==0)?null:new Object()的类型)test(Object)将始终被调用。 o的价值并不重要。

答案 2 :(得分:2)

JLS 15.25:

  
    

条件表达式的类型确定如下:

         

[...]

         
        
  • 如果第二个和第三个操作数之一是null类型和另一个操作数的类型     是一个引用类型,然后条件表达式的类型就是引用     类型。
  •     
         

[...]

  

所以

的类型
10 % 2 == 0 ? null : new Object();

是对象。

答案 3 :(得分:1)

您的答案是:运行时,因为在运行时指定参数是String的实例,所以在编译时无法找到它。

答案 4 :(得分:1)

这是一个非常好的问题。

让我试着澄清你上面写的代码。

  • 在您的第一个方法调用中
  

试验(空);

在此,null将被转换为字符串类型,因此调用test(String obj),根据JLS,您可以通过调用确信。

  • 在第二种方法调用中
  

test((10%2 == 0)?null:new Object());

将返回布尔值“true”值。因此,第一个布尔“true”值将自动转换为Boolean Wrapper类对象。布尔包装器对象在三元运算符中找到与new Object()选项的最佳匹配。并且该方法使用Object作为参数调用,因此它调用以下方法

  

public static void test(Object obj)

为了实验,您可以尝试以下组合,然后您将获得更好的清晰度。

  

test((10%2 == 0)?new Object():“stringObj”);

     

test((10%2 == 0)?new Object():null);

     

test((10%2 == 0)?“stringObj”:null);

  • 最后在你用以下代码打电话的时候。
  

试验((10%2 == 0)空:NULL);

这次又以布尔“true”值的形式返回,并且它将再次遵循与上面解释的相同的强制转换。但是这次你的三元运算符中没有new Object()参数。因此它将自动类型转换为null对象。它再次遵循与您的第一个方法调用相同的方法调用。

  • 如果您输入if .. else语句,那么在您要求代码的最后一次。然后编译器也用代码做公平的决定。
  

if(10%2 == 0){           试验(NULL);        }

此处if条件为true并且调用此代码test(null)。因此,它始终使用String作为参数调用第一个test(String obj)方法,如上所述。

答案 5 :(得分:0)

我认为你的问题是你做出了错误的假设,你的表达方式:

test((10%2==0)?null:new Object());

test((10%2==0)?null:null;

将始终调用test(null),这就是他们将通过test(Object)的原因。

答案 6 :(得分:0)

正如@Banthar所提到的,?:运算符首先为变量赋值,然后计算条件。 另一方面,您提到的if条件总是返回true,因此编译器将仅使用if-else的主体替换整个if块。

答案 7 :(得分:0)

1)test()方法由编译时参数的类型决定:

test((Object) null);
test((Object)"String");

输出:

Object called
Object called

2)编译器更智能,编译后的代码仅相当于:

test(null);

您可以使用javap -c检查字节码:

   0: aconst_null   
   1: invokestatic  #6                  // Method test:(Ljava/lang/String;)V
   4: return  

答案 8 :(得分:0)

这是Java Language Specifications对此问题所说的内容。

  

如果多个方法声明都可访问且适用   要进行方法调用,有必要选择一个来提供   运行时方法调度的描述符。 Java编程   language使用选择最具体方法的规则。

这是你的情况下的test(String)方法。

因此,如果你加上......

public static void test(Integer obj){
           System.out.println("Ingeter called");
       }

它将显示编译错误 - 方法test(String)对于OverloadingTest类型是不明确的。

就像JLS说:

  

可能没有方法是最具体的,因为有   两种或更多种最具体的方法。在这种情况下:

     

如果所有最大特定方法具有相同的签名,则:   如果其中一个最大特定方法未声明为abstract,则为   是最具体的方法。否则,所有最大的具体   方法必然被宣布为抽象的。最具体的方法是   在最大特定方法中任意选择。然而   如果和,则认为最具体的方法抛出一个已检查的异常   只有在每个的throws子句中声明了该异常时   最具体的方法。否则,我们说的方法   调用不明确,发生编译时错误。