-1
// I would like to create a variable of Class type and assign a pattern into it like that:
Class clazz = HashMap<Long,HashMap<String, Semaphore>>.class; // does not work!

// I need it in order to be able later use it in .isInstance() expression, like:
if (clazz.isInstance(myVariable)) {
    // do something
}

, because the usual "instanceof" does not work either on pattern types.

With a simple types (non-pattern types) it works:

Class clazz = Long.class; // this works fine

How can I achieve what I want that is to assign a pattern and not a simple type? If it is not allowed in Java, is there another proper way to test if the variable is an instance of a type defined as a pattern?

Alexander Samoylov
  • 1,290
  • 1
  • 15
  • 20
  • 5
    I don't think this is possible, because of run time type erasure. – Dawood ibn Kareem Oct 04 '18 at 19:12
  • 1
    _I need it in order to be able later use it in .isInstance() expression,_ - Because of [type ereasure](https://docs.oracle.com/javase/tutorial/java/generics/erasure.html) you won't be able to validate the instance at runtime against the generic type. Further reading: [Java generics type erasure: when and what happens?](https://stackoverflow.com/questions/339699/java-generics-type-erasure-when-and-what-happens) – LuCio Oct 04 '18 at 19:18
  • You want also potentially read: [Is instanceof considered bad practice? If so, under what circumstances is instanceof still preferable?](https://stackoverflow.com/questions/2750714/is-instanceof-considered-bad-practice-if-so-under-what-circumstances-is-instan) – LuCio Oct 04 '18 at 19:26

2 Answers2

1

You can't check if you have an instance of HashMap<Long,HashMap<String, Semaphore>>.

Despite the syntax implying that HashMap<Long,HashMap<String, Semaphore>> is a single thing, that type actually means:

  • A variable of this type will hold a HashMap (or null) at runtime
  • And I want the compiler to stop me if I try to put in or take out a key which isn't a Long, and to stop me if I try to put in or take out anything which isn't a HashMap whose keys and values are of that specific type.

The bit between the <>s is purely a compile time hint. There is nothing there to test at runtime.

Provided the map isn't empty, you can check if it contains any keys or values which violate the type constraints.

But, aside from you only being able to do this if the map is non-empty, and contains non-null keys and values, it doesn't tell you if it is a HashMap<Long,HashMap<String, Semaphore>>, but rather that it is a HashMap<? extends Long, ? extends HashMap<? extends String, ? extends Semaphore>>.

You can safely consume things held in such a map, but you couldn't safely add to it, other than by re-adding keys and values which were already contained in that map, or null.

Andy Turner
  • 122,430
  • 10
  • 138
  • 216
0

Generics are erased after compilation.
So even this code that is valid will not help you further :

HashMap<Long, HashMap<String, Semaphore>> map = new HashMap<>();
Class<?> clazz = map.getClass();

clazz would refer java.util.HashMap "only" and not its generics.

About your requirement, if it happens that you have to write such a code and besides by checking the types of the generics :

if (clazz.isInstance(myVariable)) {
    // do something
}

I think that should rethink your logic.
Generics are mainly designed to improve the type safety that mechanically reduces the requirement to instanceof test and downcasts.

If it is not allowed in Java, is there another proper way to test if the variable is an instance of a type defined as a pattern?

You could store the types specified in the generic instance by adding class fields in the generic class. But as you use a built in type, you are stuck.
With your own class you could capture the information in this way for example :

public class Foo<T, U, V> {

    private Map<T, HashMap<U, V>> map = new HashMap<>();
    private Class<T> tClass;
    private Class<U> uClass;
    private Class<V> vClass;

    public Foo(Class<T> t, Class<U> u, Class<V> v) {
        this.tClass = t;
        this.uClass = u;
        this.vClass = v;
    }

}

So you can use tClass, uClass, vClass to perform the required checks.
But as said, going on into this way is probably not the best thing to do.

davidxxx
  • 104,693
  • 13
  • 159
  • 179