Joel
Joel

Reputation: 2824

Why don't I have to import classes not used as variable types in Java?

Here is an example:

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());
    }
}

Everything compiles and runs fine. However, when I move map.keySet() to another variable:

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());
    }
}

I get an error thrown at me:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
    Set cannot be resolved to a type
    at Test.main(Test.java:12)

I understand why I get an error for the second example, but why don't I get an error for the first? How does the java compiler know what map.keySet() returns without importing java.util.Set?

I've seen this behavior in other programming languages too, notably C++.

Upvotes: 2

Views: 433

Answers (5)

Cyrille Ka
Cyrille Ka

Reputation: 15523

The java compiler knows what map.keySet() returns because it is in the definition of HashMap.

public Set<K> keySet() { ... }

When HashMap was compiled, the compiler knew what was Set<> because there was an import statement in the source of HashMap for that (or rather, in that case, no import statement was needed because Set is in the same package as HashMap).

Then the compiler put this information in the compiled class file, as the return type of the "signature" of the method.

When you are using map.keySet(), you don't have to specify the return type of the method because the compiler has access to the class file of HashMap and therefore it knows all it needs.

Upvotes: 1

Andy Thomas
Andy Thomas

Reputation: 86391

In the first case, the type of map.keySet() is known. The return type is part of the contract of the method.

In the second case, the unqualified name Set is not enough to identify the type of the variable keys. The fully qualified type name is not inferred from the right-hand-side of the assignment. You can either fully qualify the name, or import it.

Upvotes: 0

surfealokesea
surfealokesea

Reputation: 5116

Because in first example keySet() is executed in HashMap object, not in your main, that's why you don't need to declare it.

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1500155

I understand why I get an error for the second example, but why don't I get an error for the first? How does the java compiler know what map.keySet() returns without importing java.util.Set?

It knows that either way - it's part of the metadata in the bytecode.

What it doesn't know is what you mean by Set<String>. An import just changes the meaning of a name in your source code - and that name doesn't appear in your source code in the first piece of code.

From section 7.5 of the JLS:

An import declaration allows a named type or a static member to be referred to by a simple name (§6.2) that consists of a single identifier.

You don't refer to the simple name in your first example, so the benefit described above is irrelevant.

To put it another way, the import just allows this line (which would be valid in your second sample):

java.util.Set<String> keys = map.keySet();

to be written as:

Set<String> keys = map.keySet();

That's all it does.

Upvotes: 5

SLaks
SLaks

Reputation: 887355

The import directive is a purely syntactic feature which allows you to refer to a type without qualifying it with the package name.

Since your first code never refers to a the Set type, it works fine without the import.

import has nothing to do with using a class; it only affects how you can write its name.

Upvotes: 2

Related Questions