OCJP的赋值问题:为什么无法将int传递给short?

4

我有两段代码。一段可用,另一段不行,但两者似乎都做着相同的事情。这段代码是可用的:

short s=7;

但下面的代码却不行。反而会出现错误:

无法将int赋值给short

我知道整数字面量默认为int,但如果可以直接赋值,那么为什么在传递给方法时不行呢?
class Demo1{
    public static void main(String[] args){
        new Demo1().go(7);
    }
    void go(short s){System.out.println("short");}
}
5个回答

1

对于赋值和方法重载解析,规则是不同的:

对于赋值操作,JLS规定如下:

此外,如果表达式是类型为byte、short、char或int的常量表达式(§15.28):

如果变量的类型是byte、short或char,并且常量表达式的值可以表示为变量类型的值,则可以使用缩小的原始转换。

对于重载解析,JLS 15.12.2.2. 规定如下:

如果同时满足以下两个条件,则方法m适用于子类型:

对于1 ≤ i ≤ n,要么:
* Ai <: Si (§4.10),或者
* Ai可以通过未经检查的转换(§5.1.9)转换为某些类型Ci,且Ci <: Si。

这里的Ai是传递给方法的参数类型(在你的情况下为int,因为7是int字面量)。Si是方法形式参数的类型(在你的情况下为short)。Ai <: Si表示Ai是Si的子类型。int不是short的子类型(相反是真的),所以编译器不接受new Demo1().go(7);。


但是,“short”真的是“int”的子类型吗?如果是这样,我应该能够将“short []”分配给“int []”。程序无法编译。 但是,您可以将“Integer []”分配给“Number []”,因为“Integer”是“Number”。 然而,除了“int”可以容纳任何“short”外,两者之间没有这样的关系,但我认为它们不是子类型! - Sarabjeet
对于原始类型,它与继承不是相同的子类型关系。 它在这里定义(http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.10)。 - Eran
JLS是不错且全面的,但我必须阅读全部内容才能通过认证吗?我时间紧迫,请帮忙!当然,一旦我开始工作,我可以阅读JLS,但我必须阅读全部内容才能通过OCJP吗?谢谢。是的,我知道OCJP会问一些棘手的问题。 - Sarabjeet
1
@Sarabjeet 每当我遇到不理解的行为时,我都会搜寻 JSP。你不可能指望自己读完整本 JLS(或者,任何 Java 书籍)并记得所有内容。 - Eran

0

您必须将整数转换为短整型,如下所示:

short s = (short) 7;

这应该可以工作。

你的代码应该像这样:

class Demo1{
    public static void main(String[] args){
        new Demo1().go((short) 7);// change here.
    }
    void go(short s){System.out.println("short");}
}

在直接赋值时进行隐式转换而在方法调用期间不进行隐式转换的原因是由于赋值上下文和调用上下文之间的差异,这些差异可以通过您所处的情况自我解释。这就是Java编程语言的工作方式。

我认为这是一个积极的因素,因为它有助于编译器确定您正在调用哪个方法,如果您的某些方法具有相同的名称但参数的数据类型不同。通过将给定值转换为参数中的数据类型,编译器将能够区分您正在调用的方法,如果在参数中发生隐式转换,编译器将如何知道将其转换为哪种数据类型,如果您有两个名称相同但参数不同的方法,例如int和short。


我知道在将7传递给short时进行强制转换会起作用,但是直接赋值时,编译器提供了隐式转换以使赋值起作用,那么为什么在方法调用时它不提供相同的转换呢? - Sarabjeet
首先,我们都知道整数字面量默认为 int 类型。因此,字面量 7 的类型为 int,即使它可以分配给 shortbyte,因为它在两者的范围内。但是,编译器提供了隐式转换。这就是为什么 short s=7 可以编译,因为它实际上被隐式转换为 short s=(short)7, 但是当涉及到方法调用时,上述方法调用不提供相同的隐式转换。常识认为应该有,但实际上没有。 错误将是 go(short) cannot be applied to int - Sarabjeet
方法匹配,即决定在编译时为特定方法调用选择哪个方法[在方法重载的情况下],是在编译时决定的。所以我想知道为什么它不能在编译时决定 7 是否适合 short,并因此将其隐式转换。我的意思是,在将字面值分配给 short 变量时,它执行相同的操作。 - Sarabjeet
我认为这是一个积极的因素,因为它可以帮助编译器决定你正在调用哪个方法,如果你的一些方法具有相同的名称但参数中的数据类型不同。通过将给定值转换为参数中的数据类型,编译器将能够区分你正在调用哪个方法。如果在参数中发生隐式转换,编译器将如何知道要将其转换为哪种数据类型,如果你有两个名称相同但参数不同的方法,比如int和short。 - rert588
谢谢,你的观点确实有道理,但为了OCJP考试的缘故,我需要某种规则。据我所知,一般规则是:当两者均可行时,首选“原始类型扩展”,其次是“装箱自动转换”;而当存在两种方法时,则优先选择“装箱自动转换”,而非“变长参数”。我希望我们能够为OCJP考试定义一种通用规则。如果您能帮忙解答,我将不胜感激。 - Sarabjeet

0
在一个赋值语句中,编译器清楚地知道将哪个参数进行转换。
short s=7; //here is no doubt that it must be cast to short

在方法调用中,可能是虚拟的,这是不可确定的。
go(7); //here it could be anything

编译器尝试找到与之类型兼容的签名,即...
void go(Integer i); // via auto boxing
void go(Number i); // autoboxing and supertype

它不尝试转换类型,即以下内容不起作用:
void go(short s); // short is neither a super type nor a subtype of int
void go(byte b); // byte is neither a super type nor a subtype of int

我不太希望go(new Integer(7))调用void go(Short s),对于所有没有类型层次关系的类型都是如此。


据我所见,一般规则是当两种选项都存在时,“原始类型扩展”优先于“自动装箱”,而当两种方法都存在时,“自动装箱”优先于“可变参数”。我希望我们能为OCJP考试定义一个通用的规则。如果您能帮忙,感谢您。 - Sarabjeet

0

该语言允许在赋值上下文中将常量int表达式转换为short类型。但在方法调用上下文中不适用。相关JLS章节为JLS §5.2JLS § 5.3

对于赋值上下文,我将列出确切的语句:

此外,如果表达式是类型为byteshortcharint的常量表达式(§15.28):

如果变量的类型是byteshortchar,并且常量表达式的值可以表示为变量的类型,则可以使用缩小原始转换。

虽然没有调用上下文转换的规则。这就是为什么你必须明确告诉编译器进行转换,通过类型转换:new Demo1().go((short)7);

至于为什么会有这样的行为,我们想到的原因只能是猜测(真正的答案只有语言设计者知道)。


0

第一个是变量初始化。 第二个是将参数传递给方法。 在方法中,您必须传递变量的确切数据类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接