How to create Integer object within a class with Generic Integer - java

When I tried the following codes, I get an error:
unexpected type
required: class
found: type parameter Integer where Integer is a type-variable
class myClass <Integer>
{
public void myMethod()
{
Integer q = new Integer(5); //Not allowed
}
}
However, the following codes are allowed
class myClass
{
public void myMethod()
{
Integer q = new Integer(5); //Allowed
}
}
Can someone explain why am I not allowed to create Integer object within the class anymore when I declare the class with generic Integer ?
Que: Is it still possible and how to create Integer objects within the class in this case?

You declared Integer to be the generic type parameter, which hides the fact that the class Integer was in scope. You can do one of several things to get around this.
Choose a different name for the generic type parameter, e.g. T.
class myClass<T>
Or, fully-qualify the references to the class Integer.
java.lang.Integer q = new java.lang.Integer(5);

I think you have a wrong idea of what a parametrizable class is.
To declare a parametrizable class:
class myClass <T>
{
T myGenericVariable;
Integer i;
public void myMethod()
{
this.i = new Integer(5);
}
}
How to use it:
myClass mc = new myClass<String>();//String or whatever you want

Related

Get .class Object of Generic Class

I am tying to save the Type of a generic value, because i can't get it at runtime:
public class A<T> {
private final Class<T> genericType;
public A(Class<T> genericType) {
this.genericType = genericType;
}
public Class getGenericType() {
return genericType;
}
}
To make subclasses now, I use it as follows:
public class B extends A<String> {
public B() {
super(String.class);
}
}
Note thet the super()'s parameter type matches (by compile timne check) to the A's generic type.
That works fine. But if i want to have it with a Map, i cannot get the correct class object:
public class C extends A<Map<String, String>> {
public C() {
super(Map.class); // does not match Map<String,String>
super(Map<String,String>.class) // no valid java expression, i dont know what
}
}
Sooo anyone got a tip to help me out of this misery?
Best i could do currently, is to give up the strong typing in A:
public class A<T> {
// old: private final Class<T> genericType;
private final Class genericType; // note the missing generic
public A(Class genericType) { // here as well
this.genericType = genericType;
}
public Class getGenericType() {
return genericType;
}
}
I am not sure whether this satisfied your requirement, but you can do something similar as bellow, see How to get the class of a field of type T?
import java.lang.reflect.*;
import java.util.*;
public class GenericTypeTest{
public static void main(String []args){
B b = new B();
System.out.println("B is a " + b.getGenericType());
C c = new C();
System.out.println("C is a " + c.getGenericType());
}
}
class A<T> {
public Class getGenericType() {
Object genericType = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
if(genericType instanceof ParameterizedType){
genericType = ((ParameterizedType)genericType).getRawType();
}
return (Class<T>) genericType;
}
}
class B extends A<String> {
}
class C extends A<Map<String,String>> {
}
this will get output something like
B is a class java.lang.String
C is a interface java.util.Map
There is just one Class object representing Map at runtime, which you can get by evaluating Map.class. There are no separate Class objects representing Map<String, String> or Map<Integer, Integer> or whatever at runtime. If you just want to take Map.class, which normally has type Class<Map>, and force that into Class<Map<String, String>>, you could do that via some unchecked casts:
super((Class<Map<String, String>>)(Class<?>)Map.class);
But whether that would do what you want depends on what you expect to do with your variable of type Class<T>, genericType, which you have not shown. For example, if you will use its .isInstance() method to check whether an object is an instance of T at runtime, know that since we don't know the generic type arguments of objects at runtime, we will only be able to check the raw type of the object, and not its type argument. This is the same reason why you can do foo instanceof Map or foo instanceof Map<?> but not foo instanceof Map<String, String>.
Or, maybe you want to use its .cast() method to do a type check on an object at runtime, which normally throws an exception if the object is not an instance of the Class's class, and if it is, it returns the object, but with compile-time type T. But here, again, you can't check at runtime whether an object is an instance of a parameterized type like Map<String, String>; you can only check whether it is a Map. So by it allowing you to get the result as type T without warning might be unsafe, as you get a compile-time expression of type Map<String, String>, but it might really be a Map<Integer, Integer>. This is the same reason why an unchecked cast like (Map<String, String>)foo causes an unchecked cast warning. It is unsafe and might cause unexpected problems elsewhere in your code.

Are the type of java generics decided at compile time? And why can we change the type at runtime?

Does the output of this code make sense to you? It gives this output:
class java.lang.String
class java.lang.Integer
class java.lang.String
So it seems that the type is assigned to Integer when the test object is initiated, yet the type of member is changing during runtime. Why does this occur?
public class Test {
public static void main(String[] args) {
MyType<Integer> test = new MyType<Integer>("hello", 1, "world");
}
public static class MyType<T> {
private T member;
public MyType(Object o, Object o2, Object o3) {
T t = (T) o;
member = (T) o2;
System.out.println(t.getClass());
System.out.println(member.getClass());
member = (T) o3;
System.out.println(member.getClass());
}
}
}
The seemingly odd behavior is due to how java implements generics using type erasure. You can view this question for a more detailed explanation of type erasure but I can summarize it's effect in this scenario.
When calling
new MyType<Integer>("hello", 1, "world");
it appears the constructor attempts to cast both the int type "1" and the String type "world" to the "member" instance variable, which would be of type Integer:
member = (T) o2;
System.out.println(t.getClass());
System.out.println(member.getClass());
member = (T) o3;
However at run time, this isn't what is happening - with type erasure, at compilation, the types used in generics get "erased" and the inner class code gets compiled to:
public static class MyType {
private Object member;
public MyType(Object o, Object o2, Object o3) {
Object t = o;
member = o2;
System.out.println(t.getClass());
System.out.println(member.getClass());
member = o3;
System.out.println(member.getClass());
}
}
So at this point Object types are being assigned to Object types which will not cause any errors - although the actual referenced objects are of different types (o2 is an int, o3 is a String). That's why member.getClass() returns the actual class of the reference (in this case Integer and String).
So when does the generic parameter
<Integer>
actually come into play? When it is used. I made a minor modification to your code that attempts to access the member field and call an Integer method on it:
public class Test {
public static void main(String[] args) {
MyType<Integer> test = new MyType<Integer>("hello", 1, "world");
// attempting to use the member variable as an Integer
System.out.println(test.getMember().doubleValue());
}
public static class MyType<T> {
private T member;
public MyType(Object o, Object o2, Object o3) {
T t = (T) o;
member = (T) o2;
System.out.println(t.getClass());
System.out.println(member.getClass());
member = (T) o3;
System.out.println(member.getClass());
}
public T getMember() {
return member;
}
}
}
Attempting to run the code throws the following exception:
Exception in thread "main" java.lang.ClassCastException:
java.lang.String cannot be cast to java.lang.Integer
When test.getMember().doubleValue() gets called the compiler then tries to cast it to your Integer generic parameter - but the argument you had set it to ("world") is a String so a ClassCastException gets thrown. The compiled call would look something like this:
System.out.println(((Integer) test.getMember()).doubleValue());
Note that the same thing happens if "member" was public and I skipped using a getter (as in test.member.doubleValue()).
So essentially your ClassCastException was delayed because the types get erased. The Oracle docs on this were useful as a resource: https://docs.oracle.com/javase/tutorial/java/generics/genMethods.html. If anyone knows better, please correct me on my explanation of type erasure if necessary. Thanks.
It does not change, you cast the object to another type, but the reference is the same. Your code is wrong in the sense that you upcast an object to the wrong type. But since the referance is to the original object, getClass will return the type of the real object.
If you use those variables, you might crash the app.

Java generic types inheritance error

In Android Studio I have the following error:
java: incompatible types: java.lang.Object cannot be converted to
java.lang.String
I think b1 and b2 should be behaving the same, but they are not.
Is there a way to make them behave the same (without changing their type of course)?
Here is the code typed:
public class Test
{
class A<T>
{
T t;
T getT()
{
return t;
}
}
class AS extends A<String>
{
}
class B<T> extends AS
{
}
B<Object> b1;
B b2;
public void test()
{
String t3 = b1.getT();
String t4 = b2.getT();
}
}
The problem is that B is a parameterized type, but b2 is declared to have raw B as its type.
You demonstrate with b1 that B's type parameter is distinct from A's, even though they have the same name, so that B's type parameter has nothing to do with the return type of the getT() method inherited from A<String>. When you use a raw type, however, you get the full erasure of that type, including of its supertypes.
Since's A's type parameter is unbounded, its erasure produces type Object, and therefore this is the type of b2.getT(). Of course, Object is not assignable to String.
You can resolve this in at least two ways:
Don't use a raw type for b2. If you don't care about its type parameter then use B<?>. Or,
Remove class B's type parameter. It's not used for anything in your example, so that would be the cleanest thing to do. Just because its superclass is generic does not mean that B has to be.
I think that B without the template object is an incomplete class as opposed to B<Object>. This is why when you are calling it getT() you are actually calling A.getT().
This why in the code below, only String t3 = b2.getT(); fails to compile.
static class A<T>
{
T t;
T getT()
{
return t;
}
}
static class AS extends A<String> {}
static class B<T> extends AS {}
static class C extends B<Object> {}
static A a;
static B<Object> b1 = null;
static B b2 = new B();
static C c = new C();
static void test()
{
Object tt = a.getT();
String t2 = b1.getT();
String t3 = b2.getT();
String t4 = c.getT();
}
The issue here is that getT() returns the object. What you need to do is implement a method toString() that gives the value of the T Object in terms of a String (or just change the types for t3 and t4 to T so the declaration comes out to T t3 = b1.getT(); and T t4 = b2.getT();).
In addition, you should do the following instead of the code you have for B.
B<T> b1;
B<T> b2;
Note that you need to initialize B to something before you can call b1.anyMethod() or else you'll get a NullPointer Exception.

Java Generics with Static Factory methods

I'm trying to create a Variable class that can represent either an Integer or Double value using generics.
Below is the code that I have tried. Because of erasure I use an enum to store the intended type of the Variable and then try and use this to initialise the value to the correct type.
public class Variable<T> {
private enum Type {INTEGER, DOUBLE};
private Type type;
private T value;
public static Variable<Integer> createAsInteger() {
return new Variable<Integer>(Type.INTEGER);
}
public static Variable<Double> createAsDouble() {
return new Variable<Double>(Type.DOUBLE);
}
private Variable(Type type) {
this.type = type;
if(type == Type.INTEGER) {
value = new Integer(0);
} else {
value = new Double(0.0);
}
}
public static void main(String[] args) {
Variable.createAsInteger();
Variable.createAsDouble();
}
}
However when I compile it I get the following message...
error: incompatible types: Integer cannot be converted to T
value = new Integer(0);
and likewise for the Double.
Can anyone explain why this is happening and if there is a way round this without having to write two separate classes, one for Integer and one for Double?
Edit
Thanks for all your answers...based on them I now realise there are better ways of doing this. However I'm trying to get my head round why this approach isn't working so that I don't make the same mistake in the future.
When I define my class as public class Variable<T extends Number> as suggested, I still get the same error.
Your architecture seems to defile the concept of generics.
The simplest way would be to have an upper bound in your type parameter:
class Variable<T extends Number> {...}
Then you can have a generic factory method creating a Variable<X> based on your required class:
static <X extends Number>Variable<X> create() {
return new Variable<X>();
}
You can then invoke it as:
Variable.<Integer>create(); // returns an instance of `Variable<Integer>`
This will not limit to Integer and Double, but rather any Number.
If you have to, you can limit those choices by performing the following:
Add a parameter to your create method: create(Class<X> clazz)
Check the value of your clazz argument within the method's body:
if (!clazz.equals(Integer.class) && !clazz.equals(Double.class)) {
// TODO complain
}
Otherwise, you can ensure you use a private constructor and provide static createAs... non-generic methods such as createAsInteger etc., that would return a new Variable<Integer> etc.
The problem here is that T can be anything. What if T was for instance String, your code would amount to:
String value = new Integer(0);
You could lay out your factory methods like this:
public static Variable<Integer> createAsInteger() {
return new Variable<>(new Integer(0), Type.INTEGER);
}
Where you have a constructor like:
private Variable(T value, Type type) {
this.value = value;
this.type = type;
}
You get the error because you are typizing a method inside a generic class. You can't define some inside the T generic class.
By the way you are mistaking the design pattern.
You have to design a generic class for Variable, also the constructor must have T as argument type.
In an other class you implement the factory with the createInteger and the createDouble methods.
You can make your class inherit from Numbers and use type checking to invoke appropriate method for Integer or Double.
public class Variable<T extends Number> {
public static Variable<T extends Number> Variable<T> create(Variable<T> var){
if (var instanceOf Integer){
// Take appropriate action
}
else if (var instanceOf Double){
// Take appropriate action
}
}
}
By this, there is no peculiar need of maintaining a separate enum for Types.

Java how to set objects type

For example I have a class which starts like this
public class Class<T> {
private ArrayList<Integer> x = new ArrayList<Integer>();
In main when I do
public class Main {
public static void main(String args[]) {
Class x = new Class();
All elements in x should be of Integer type. How can I do something like Class x = new Integer/String ... Class(); So I can specify the type of the objects in x
You mean something like this?
public class MyClass<T>{
private final List<T> x = new ArrayList<T>();
Now your List is of the generic type given to MyClass.
EDIT
OP in comment - if I want to make a fonction which adds elements to x.
In order to do that you need to create a method (rather than function) that takes an item of type T and adds it to x
public void addThing(final T thing) {
x.add(thing);
}
Use Generics.
public class YourClass<T>{
private ArrayList<T> x = new ArrayList<T>();
Then in Main
public class Main{
public static void main(String args[]){
YourClass<Integer> x = new YourClass<Integer>();
}
or
YourClass<String> x = new YourClass<String>();
YourClass<T> could be then an Integer, String, or whatever.
Class<Integer> x = new Class<Integer>();
Don't use names for classes that are already defined in java.lang package.
I think you are confused by Polymorphism and Generics.
What you want:
Class x = new Integer();
is polymorphism. This statement will only be valid if Class is a base class of Integer, which is clearly not the case.
Parent x = new Child();
will work if
class Child extends Parent {
....
}
The class you have written:
public class Class<T> {
private ArrayList<Integer> x = new ArrayList<Integer>();
....
}
is a generic class but you do not use the type parameter. This is not a problem though, your class will still work. When you say:
Class x = new Class();
The type T will simply be Object. But since you do not use this type parameter, it does not have any effect.
To summarize, your statement of:
Class x = new Integer();
will never work as Integer does not have a "is a" relationship with Class. Generics is not used for this purpose.

Categories