以下是一个例子:
import java.util.HashMap;
public class Test
{
public static void main(String[] args)
{
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("leorum", 1);
map.put("ipsum", 2);
map.put("dolor", 3);
System.out.println(map.keySet().toString());
}
}
一切都编译好了。但是,当我将map.keySet()
移动到另一个变量时:
import java.util.HashMap;
public class Test
{
public static void main(String[] args)
{
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("leorum", 1);
map.put("ipsum", 2);
map.put("dolor", 3);
Set<String> keys = map.keySet();
System.out.println(keys.toString());
}
}
我收到一个错误:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Set cannot be resolved to a type
at Test.main(Test.java:12)
我理解为什么第二个例子会出错,但为什么我第一个出错呢? java编译器如何知道map.keySet()
返回的内容而不导入java.util.Set?
我也在其他编程语言中看到过这种行为,尤其是C ++。
答案 0 :(得分:5)
我理解为什么第二个例子会出错,但为什么我第一个出错呢? java编译器如何知道
map.keySet()
返回的内容而不导入java.util.Set
?
它知道这两种方式 - 它是字节码中元数据的一部分。
它不知道的是Set<String>
的含义。导入只会更改源代码中名称的含义 - 并且该名称在第一段代码中的源代码中不会出现。
导入声明允许通过由单个标识符组成的简单名称(第6.2节)引用命名类型或静态成员。
在第一个示例中,您没有引用简单名称,因此上述优点无关紧要。
换句话说,导入只允许这一行(在你的第二个样本中有效):
java.util.Set<String> keys = map.keySet();
写成:
Set<String> keys = map.keySet();
就是这样。
答案 1 :(得分:2)
import
指令是纯粹的语法特性,它允许您引用类型而不用包名称限定它。
由于您的第一个代码从未引用Set
类型,因此无需导入即可正常工作。
import
与使用类没有任何关系;它只影响你如何写出它的名字。
答案 2 :(得分:1)
java编译器知道map.keySet()
返回的内容,因为它位于HashMap
的定义中。
public Set<K> keySet() { ... }
编译HashMap时,编译器知道什么是Set<>
,因为在HashMap的源代码中有一个import
语句(或者更确切地说,在这种情况下,不需要导入语句,因为Set与HashMap位于同一个包中。
然后编译器将此信息放在已编译的类文件中,作为方法“签名”的返回类型。
当您使用map.keySet()
时,您不必指定方法的返回类型,因为编译器可以访问HashMap的类文件,因此它知道它所需的全部内容。
答案 3 :(得分:0)
因为在第一个例子中,keySet()是在HashMap对象中执行的,而不是在你的main中执行,这就是你不需要声明它的原因。
答案 4 :(得分:0)
在第一种情况下,map.keySet()
的类型是已知的。返回类型是方法合同的一部分。
在第二种情况下,非限定名称Set
不足以识别变量keys
的类型。完全限定的类型名称不是从作业的右侧推断出来的。您可以完全限定名称,也可以将其导入。