Cooli
Cooli

Reputation: 169

Java basics about generics and reflection

I am having a basic java question as I recently decided to dig into the language (I have been working exclusively with C++ for a long time and decided to expand my horizons a bit).

Assuming that I have a class called BankAccount and I try to create and object in my main, what is the difference between:

Class baCls = BankAccount.class; 

and

Class<BankAccount> baCls = BankAccount.class;

What does the baCls hold in each of the cases and what is the compiler output?

Upvotes: 0

Views: 52

Answers (2)

Andy Turner
Andy Turner

Reputation: 140319

Peter Lawrey's answer addresses your specific question, but I think that it is missing an important fact that the second case provides additional, invaluable information to the compiler.

The difference is that the first case is a raw type:

Class baCls = BankAccount.class;
     ^ Missing type parameters.

There are (at least) two consequences of this (which are general consequences of using raw types):

  1. You can't invoke producer methods like Class.nextInstance() method and assign the result to a reference of type BankAccount without a cast:

    Class baCls = BankAccount.class;
    BankAccount instance = (BankAccount) baCls.newInstance(/* required args */);
    

    However, you don't need the cast in the second case, because it is known that the nextInstance method will return an instance of BankAccount:

    Class<BankAccount> baClsSafe = BankAccount.class;
    BankAccount instance = baCls.newInstance(/* req args */);
    
  2. You lose type safety if you are putting these into collections. For instance:

    Class baCls = BankAccount.class;
    List<Class<String>> list = new ArrayList<>();
    list.add(baCls);  // Compiles fine.
    

    This appears fine, but you get a runtime exception later if you assume that the elements in the list are of the correct type:

    for (Class<String> clazz : list) {
      String instance = clazz.newInstance();  // ClassCastException.
    }
    

    because instance is actually an instance of BankAccount (assuming that has a zero-args constructor).

    In the second case, you would never be able to add this item to the list in the first place:

    Class<BankAccount> baCls = BankAccount.class;
    List<Class<String>> list = new ArrayList<>();
    list.add(baCls);  // Compiler error.
    

I would recommend that you read up about raw types, e.g. in the generics tutorial, as well as in Effective Java 2nd Ed Item 23 "Don't use raw types in new code".

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533492

In both cases baCls is a reference to the class BankAccount

The generic adds a compile time check but has no impact at runtime.

You can see the bytecode generated if you use javap -c -p or a byte code viewer in your IDE.

Upvotes: 4

Related Questions