Isn't there any way to find the class-type of a generic?
if (T instanceof String) {
// do something...
}
The above definitely does not compile.
Generics are a compile time feature. Generics add checks at compile time which may not have any meaning at runtime. This is one example. You can only check the type of the object referenced which could be a super type in code. If you want to pass the type T you have do this explicitly.
void someMethod(Class<T> tClass) {
if(String.class.isAssignableFrom(tClass))
or
void someMethod(Class<T> tClass, T tArg) {
Note: the type might not be the same,
someMethod(Number.class, 1);
It won't compile because T is not a variable, but a place holder for a class that is defined at runtime. Here's a quick sample:
public class Test<T> {
public void something(T arg) {
if (arg instanceof String) {
System.out.println("Woot!");
}
}
public static void main(String[] args) {
Test<String> t = new Test<String>();
t.something("Hello");
}
}
if you have subclass
public class SomeClass extends SomeSubclass<String>{}
and
public class SomeSubclass<T> {}
then there is a way to discover type of T by executing code
Type t = getClass().getGenericSuperclass()
if (t instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType)t).getActualTypeArguments()
// in simple cases actualTypeArguments will contain Classes, since Class implements Type
}
if your case are a bit more complex (? extends String)` take a look at org.ormunit.entity.AEntityAccessor#extractClass
If you have specific field you can just check it like below:
private <T> String someMethod(T genericElement)
{
if (String.class.isInstance(genericElement))
{
return (String) genericElement;
}
...
Related
I'm primarily a Java programmer, so this would be one of those "what is this thing from Java equivalent to in C#" questions. So, in Java, you can restrain a Class type argument at compile time to extend a certain super-class, like so:
public <T extends BaseClass> void foo(Class<T> type) {
...
}
and even
public <T extends BaseClass> T foo(Class<T> type) {
...
}
You can even chain multiple interfaces:
public <T extends BaseClass & BaseInterface1 & BaseInterface2> void foo(Class<T> type) {
...
}
How is this done in C#? I know you can use "where T : BaseClass", but this is only applicable when you have an instance T. What about when you only have a Type instance?
EDIT:
For explanation, here is what I would like to do:
ASSEMBLY #1 (base.dll):
abstract class BaseClass {
abstract void Foo();
}
ASSEMBLY #2 (sub1.dll, references base.dll):
class SubClass1 : BaseClass {
void Foo() {
// some code
}
}
ASSEMBLY #3 (sub2.dll, references base.dll):
class SubClass2 : BaseClass {
void Foo() {
// some other code
}
}
ASSEMBLY #4 (main.dll, references base.dll):
class BaseClassUtil {
static void CallFoo(Type<T> type) where T : BaseClass {
T instance = (T)Activator.CreateInstance(type);
instance.Foo();
}
}
public static void Main(String[] args) {
// Here I use 'args' to get a class type,
// possibly loading it dynamically from a DLL
Type<? : BaseClass> type = LoadFromDll(args); // Loaded from DLL
BaseClassUtil.CallFoo(type);
}
So, in this example, I don't care what class the 'type' variable represents, as long as it is derived from BaseClass, so once I create an instance, can call Foo().
The parts that are not vaild C# code (but rather some Java mockup) are the "generic" Type classes: Type<T> and Type<? : BaseClass>.
No, there is no way to enforce at compile time that a Type be assignable to a generic type. If I understand correctly, what you want is:
void Foo<T>(Type type) { ... } //compile time error if an instace typed `type` is not assignable to `T`.
Which means:
void Foo<IFormattable>(typeof(string)); //ok
void Foo<IDisposable>(typeof(string)); //compile time error
Evidently at runtime it is trival, but the language has no support for this at compile time.
From what I understood you are talking about generic type constraint
public void Foo<T>(Type type) where T:BaseClass, BaseInterface1, BaseInterface2
{
//your code
}
Here another article:Constraints on Type Parameters (C# Programming Guide)
When you define a generic class, you can apply restrictions to the
kinds of types that client code can use for type arguments when it
instantiates your class. If client code tries to instantiate your
class by using a type that is not allowed by a constraint, the result
is a compile-time error.
EDIT:
Here your example. Now if you try to call BaseClassUtil.CallFoo<T> with something different from BaseClass and his derived classes you will receive an compile error. Here full example in dotNetFiddle. So the tricky part is the restriction of your class should happen in the Util class
public static void Main(string[] args)
{
//so your LoadFromDll method should return Type. Type doesn't have generic implementation !
Type type = typeof(SubClass1);
BaseClassUtil.CallFoo<BaseClass>(type);
Type type2 = typeof(SubClass2);
//you can write BaseClassUtil.CallFoo<SubClass2>(type2); if you want
BaseClassUtil.CallFoo<BaseClass>(type2);
}
public class BaseClassUtil
{
public static void CallFoo<T>(Type type) where T : BaseClass
{
T instance = (T)Activator.CreateInstance(type);
instance.Foo();
}
}
public class TestClass
{
public int ID { get; set; }
}
public abstract class BaseClass
{
public abstract void Foo();
}
public class SubClass1 : BaseClass
{
public override void Foo()
{
Console.WriteLine("SubClass 1");
}
}
public class SubClass2 : BaseClass
{
public override void Foo()
{
Console.WriteLine("SubClass 2");
}
}
Say, i have a generic type as below
public class GenericType<T> {
private T someVar;
public void setVar(T var) { this.someVar = var; }
//Rest of the code
}
I want to allow it to take only specific types(String/Integer/Double). I know about bounded wildcards but they don't help me here. In setVar(), I can check the instanceof and throw an Exception if type is not Integer/String etc. Is this the best way to do it?
I have the same problem when doing operations on this type. Depending on the type, I want to do different operations. Inheritance and bounded wildcards seem like the way to go in general for this kind of problem but these are primitive wrappers.
Using Inheritance:
Parent.java
public abstract class Parent<T> {
public abstract void display(T t);
}
ChildString.java
public class ChildString extends Parent<String> {
#Override
public void display(String t) {
// Do something here...
}
}
ChildInteger.java
public class ChildInteger extends Parent<Integer> {
#Override
public void display(Integer t) {
// Do something here...
}
}
ChildDouble.java
public class ChildDouble extends Parent<Double> {
#Override
public void display(Double t) {
// Do something here...
}
}
And access the class child rather than you directly access the parent class.
Update
Here another example:
GenericType.java
public class GenericType {
public void display(Object t) {
String msg;
if(t instanceof String) {
msg = "String";
} else if (t instanceof Integer) {
msg = "Integer";
} else if (t instanceof Double) {
msg = "Double";
} else {
msg = "Another Object";
}
System.out.println(msg);
}
}
SpecificGeneric.java
public class SpecificGeneric {
public static void main(String[] args) {
GenericType basicType = new GenericType();
basicType.display(new String());
basicType.display(new Integer(1));
basicType.display(new Double(0.1));
}
}
You cannot (more than extends something, but in your case you want few unrelated types, so it does not help).
What you can, is check instance passed to method (you already know it). If you want one instace of generic class for eg. String another for Integers, but don't allow eg. Point2D, you can make constructor with parameter Class clazz and check when constructing whether its allowed.
If you are more paranoid, you can store that clazz and in all function compare whether parameter is actualy that class.
This way, you can still create MyClass, but cannot create instance with this type. (But you can cast it, co its not fool proof)
Inferring the desired type say GenericType<Double> and using instanceof when neccesary is the quickest and easy option. Alternatively overload setVar(..) to accept the restricted types in your Generic class.
public static class GenericType<T>
{
private T someVar;
public void setVar(String var)
{
this.someVar = (T) var;
}
public void setVar(Integer var)
{
this.someVar = (T) var;
}
public void setVar(Double var)
{
this.someVar = (T) var;
}
}
I'm wondering what are the options to specialize generic types in Java, i.e. in a templated class to have specific overrides for certain types.
In my case I was a generic class (of type T) to return null usually, but return "" (the empty string), when T is the String type, or 0 (zero) when its the Integer type, etc.
Merely providing a type-specific overload of a method produces a "method is ambiguous" error:
e.g.:
public class Hacking {
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
Bar<String> barString = new Bar<String>();
// OK, returns null
System.out.println(barInt.get(new Integer(4)));
// ERROR: The method get(String) is ambiguous for the type Bar<String>
System.out.println(barString.get(new String("foo")));
}
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
}
Is the only option to subclass the generic class with a specific type (see StringBar in the following example?
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
StringBar barString2 = new StringBar();
// OK, returns null
System.out.println(barInt.get());
// OK, returns ""
System.out.println(barString2.get());
}
public static class Bar<T> {
public T get() {
return null;
}
}
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
}
Is this is the only way, it's a bit of a pain to have to create a subclass for every type I want to specialize instead of an overload of get() in the Bar class.
I'm guessing I could check the instanceof in the Bar.get() method, e.g.
T get(T t) {
if (t instanceof String) return "";
if (t instanceof Integer) return 0;
else return null;
}
However I've been taught to avoid instanceof and use polymorphism when possible.
All things considered, the concensus appears to be that the StringBar method mentioned in the question is the only way to go.
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
Generics in Java are very different from templates in C++ in this respect. It is not possible to write a specific version of a generic class to do something different for a particular case, as C++ can do. It is also not possible to determine at run time what T is - this is because that information is not passed into the byte code (object code) and so doesn't even exist at runtime. This due to something called "type erasure".
BarString and BarInt would be the obvious way of doing this, but there are improvements you can make. For example you can write a generic Bar to cover the common cases, and then write specialized BarString and BarInt to implement special cases. Ensure that the instances can only be created through a factory, which takes the class of the object to be processed:
class Bar<T> {
class BarString extends Bar<String> {
// specialist code goes here
}
static Bar<T> createBar(Class<T> clazz) {
if (clazz==String.class) {
return new BarString();
} else {
return new Bar<T>;
}
That probably won't compile, but I don't have the time to work out the exact syntax. It does illustrate the principle.
The compiler is actually correct, because the following code is compile-time checked (Bar<String> barString = new Bar<String>();) when compiled, from
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
to
public static class Bar<String> {
public String get(String x) {
return null;
}
public String get(String x) {
return "";
}
}
and is ambiguous as you can't have 2 identical methods with the same return types and the same parameter arguments.
See an explanation by Jon Skeet's:
What is the concept of erasure of generics in java?
Java Generics - Types erasures - when and what happens?
You can subclass Bar<T> and create StringBar (note I removed the static keyword) and override get() method.
public class BarString extends Bar<String> {
#Override
public String get(String x) {
return "";
}
}
Generics in Java aren't made for specialization. They're made for generalization! If you want to specialize for certain types, you should be specializing...through a subclass.
Often you don't need to do something in a specialized manner however. Your StringBar example is kind of contrived because you could have this:
public class Bar<T> {
private final T value;
public T get() {
return value;
}
}
I don't see why you need to specialize for a String here.
I have a generic class, says :
MyClass<T>
Inside a method of this class, I would like to test the type of T, for example :
void MyMethod()
{
if (T == String)
...
if (T == int)
...
}
how can I do that ?
Thanks for your help
You can't, normally, due to type erasure. See Angelika Langer's Java Generics FAQ for more details.
What you can do is pass a Class<T> into your constructor, and then check that:
public MyClass<T>
{
private final Class<T> clazz;
public MyClass(Class<T> clazz)
{
this.clazz = clazz;
}
public void myMethod()
{
if (clazz == String.class)
{
...
}
}
}
Note that Java does not allow primitives to be used for type arguments though, so int is out...
Because of type erasure you can't... mostly. But there is one exception to that. Consider:
class A {
List<String> list;
}
public class Main {
public static void main(String args[]) {
for (Field field : A.class.getDeclaredFields()) {
System.out.printf("%s: %s%n", field.getName(), field.getGenericType());
}
}
}
Output:
list: java.util.List<java.lang.String>
If you need the class object, this is how you generally handle it:
public <T> T createObject(Class<T> clazz) {
return clazz.newInstance();
}
ie by passing the class object around and deriving the generic type from that class.
Additionally to cletus one exception I've mine: super type tokens. The super type token will preserve the type information.
new Type<Set<Integer>>() {}
The type information can be retrieved with Class.getGenericSuperClass.
if (object instanceof String)
System.out.println("object is a string");
As it was already stated you can get only generics-related information available at the static byte code level.
It's possible to resolve type arguments values and check if one type may be used in place of another then.
if you have subclass B extends A that should match, too, the approach clazz == A.class. Doesn't work. You should then use A.class.isInstance(b) where b is an object of type B.
If you want to do different things for different types would it still be generic?
What is the best way to retrieve the runtime value of a generic parameter for a generic class? For example:
public class MyClass<T> {
public void printT() {
// print the class of T, something like:
// System.out.println(T.class.getName());
}
}
So if I call
new MyClass<String>().printT()
it will print "String"
You don't. Due to type erasure that information is (mostly) lost at runtime. If you really need the class this is what you do:
public class MyClass<T> {
private final Class<T> clazz;
public MyClass(Class<T> c) {
if (c == null) {
throw new NullPointerException("class cannot be null");
}
clazz = c;
}
public void printT() {
System.out.println(clazz.getName());
}
}
and then you have access to it.
To achieve that, you need to add the type info, since type erasure means that T's type is not available.
public class MyClass<T> {
private final Class<T> clazz;
public MyClass(Class<T> clazz) {
this.clazz=clazz;
}
public void printT() {
// print the class of T, something like:
System.out.println(this.clazz);
}
}
Java doesn't have that information at runtime, because of type erasure. You would need to pass the type as a constructor parameter to the class when the object is instantiated. There are some libraries and languages which can help you to do that with less typing: Guice can do it and also Scala can do it.
As mentioned before, you can't obtain type information at that example because of type erasure.
However, you can redesign your classes hierarchy in order to have a generic superclass/interface and make child classes directly define type parameter at their definitions:
package com;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
public class AAA {
public static void main(String[] args) throws Exception {
Object target = new MyClass<Integer>() {}; // child class that explicitly defines superclass type parameter is declared here
ParameterizedType type = (ParameterizedType) target.getClass().getGenericSuperclass();
System.out.println(Arrays.toString(type.getActualTypeArguments()));
}
}
class MyClass<T> {
}