Well,
I have been doing Java Generics for going on three years now. I can cite a dozen Stack Overflow posts about "Reifying Java Generics" here: SO1, SO2, SO3. Most importantly, if you are intending to write Java for years and years, you must know that the "Generic Type Parameter"
are simply NOT ACCESSIBLE at Run-Time without fields, or extra methods to retrieve them. Java Generics (The syntax that looks like: STUFF<TYPE>
with the greater-than, less-than symbols is STRICTLY A COMPILE-TIME FEATURE). At RunTime, the JRE simply has no idea what the Type of the Type-Parameter is - and all it can do is throw ClassCastException
if an attempt to misuse occurs.
NOTE: 'Misuse' the generic type such that it throws ClassCastException
should sound odd if you are thinking that the JRE does not know and does not care what the type of the type parameter is. Mostly, the way exceptions are thrown, is such that if the code you write inside of a generic makes presumptions, and if it has made faulty presumptions, then this exception will throw.
Read Sun / Oracle's "To Do" list about "Reifying Generic Type Parameters." Also, most importantly, this concept has a very real name that you should read about all the time in Java - and it is called "Run Time Type Erasure" The solution posted before this Stack Overflow Answer says to use try-catch (ClassCastException)
blocks, which is, actually, a valid answer.
ALSO: The answer about creating this type of TypedPredicate<T> extends Predicate<T>
is not the correct answer if you intend to use your TypedPredicate<T>
in any way that expects to allow Java Lambda Syntax to work with it. When you add the following method:
public interface TypedPredicate extends Predicate { Class
getTestType(); }
You will not be able to use the syntax @FunctionalInterface
- which is one of the primary benefits of the class java.util.function.Predicate<T>
Furthermore, there is a more severe problem in that, the Type of T
is not accessible to the programmer and is not known at RunTime by the JRE
You see this part right here (since the answer has a green check mark):
{ Class<T> getTestType(); }
// Can you answer what you would write inside the method body of this
// 'extra-method' that you have added to Predicate<T> ???
The following implementation of a class that extends "Predicate"
cannot be instantiated without a constructor. It cannot be called a "@FunctionalInterface"
and lambda-expression
cannot be used to create them:
// @FunctionalInterface (Commented Out)
public class TypedPredicate<A> implements Predicate<A>
{
public boolean test(A a) { return pred.test(a); }
// This is how the "Class of A" becomes accessible. It this
// version it is a public (and final) field.
public final Class<A> className;
// NOTE: This is the most important part here, the class of
// Variable-Type Parameter 'A' must be passed as a parameter
// to the constructor. The programmer *LITERALLY* has to tell
// the code what type 'A' actually is! This is the *BANE* of
// what I call the Java-Erasure-Fiasco programming.
public TypedPredicate(Predicate<A> pred, Class<A> className)
{
this.pred = pred;
this.className = className;
}
// Again, because a constructor is necessary, this cannot be
// called a "Functional Interface" and it will not work very
// much like a java.util.function.Predicate<T>, but it will
// indeed implement the interface.
}
The best solution would realign whatever logic you have such that you do not need to guess
what type a Predicate is! The next best thing would be to try the catch (ClassCastException)
version that was suggested in the previous answer.
FINALLY: This idea regarding the java.lang.Class.isAssignableFrom(...)
has the right idea behind it - but only if you actually have the Class<T> clazz
as an instance in front of you, so to speak. The only way to get an instance of Class<T>
would be to pass it to a constructor as in the example I have posted.