This question already has an answer here:
How to check a (unchecked) cast in java?
(1 answer)
Closed 3 years ago.
I want to write universal function, which can make an object of any type by name and return it as its own type, not as Object type.
import java.lang.reflect.Constructor;
public class Test{
static <T extends Object> T makeObject(String className) {
try {
Class<?> c = Class.forName(className);
Constructor<?> constructor = c.getConstructor();
Object object = constructor.newInstance();
return (T)c.cast(object);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
//without casting
String obj = makeObject("java.lang.String");
System.out.println(obj.getClass());
}
}
It works fine, but I get the warning
Type safety: Unchecked cast from capture#4-of ? to T
What to do to fix the problem without adding #SuppressWarnings("unchecked")?
If you want this to be safe you need to pass the class as a parameter, otherwise there is no way you're going to get what you want. Your code will compile whatever I ask the return type to be, but will fail at runtime if the type isn't compatible.
Try it out:
String s = makeObject("java.lang.String"); // compiles and runs
Integer i = makeObject("java.lang.String"); // compiles but fails at runtime with java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
A safe version would look like that
static <T> T makeObject(Class<T> clazz) throws Exception {
return clazz.getConstructor().newInstance();
}
but that suggests you already know what type you want to have instead of just getting a class name from a file or something.
Related
This question already has answers here:
Is it possible to get type parameters at runtime?
(3 answers)
Closed 5 months ago.
I've read a lot of other questions out there, but I cannot find one with similar case as mine. Assume I have the following code:
public class TestClass
{
public Class clazz;
public TestClass(Object input)
{
this.clazz = ........ ?? // how to get String.class from "input" parameter?
}
public static void main(String[] args)
{
List<String> all = new ArrayList<>();
new TestClass(all);
}
}
The constructor of TestClass has 1 parameter which is an Object type. At runtime, it receives a variable of type List, but the actual instance is an ArrayList. Inside the TestClass constructor, I want to extract the generic type of that ArrayList from input parameter. The expected result should be String.class object, which can be stored in clazz variable.
This is not possible in Java because the generic type is only saved during compile type. You cannot get type runtime as you are expecting. Internally it will create every type as Object only which is base class for all java class. This concept is called as type erasure. You can read about it more here.
One way you can do is something like this. It will work only if List is not empty.
public class TestClass{
public Class clazz;
public TestClass(List<String> input) {
if (input instanceof ArrayList) {
if(!input.isEmpty()){
this.clazz = input.get(0).getClass();
System.out.println(clazz);
}
}
}
public static void main(String[] args) {
List<String> all = new ArrayList<>();
all.add("21");
new TestClass(all);
}
}
I am a beginner in Java.
How do I initialize a list of a class type received as a parameter? I have a method that takes in a Class<?> argument as a parameter, and I want to create a list of this class type.
public List<?> returnList(Class<?> listItemType) {
//create a list of type "listItemType"
//populate the list
//return the list
}
I have tried doing
List<listItemType> list = new ArrayList<listItemType>()
but VSCode shows listItemType cannot be resolved to a type.
I have also tried <? extends listItemType>, <listItemType.getName()> and the below code, but they don't seem to work either.
Object obj = listItemType.getDeclaredConstructor().newInstance();
List<obj.getClass()> = new ArrayList<obj.getClass()>();
If you make the method a generic method with a named type parameter it will work. For example:
public <T> List<T> returnList(Class<T> listItemType) {
List<T> list = new ArrayList<>();
try {
list.add(listItemType.getConstructor().newInstance());
} catch (ReflectiveOperationException ex) {
// do something
}
return list;
}
Note that various exceptions are possible. For example, the class may not have a suitable constructor, constructor may not be accessible, or it may throw exceptions. A complete version of this method should do something appropriate, or declare the relevant exceptions so that they can propagate to the caller and beyond.
Without the named type parameter T you don't connect the wildcard in List<?> to the wildcard in Class<?>. So the compiler wouldn't know that returnList(Foobar.class) is intended to return a List<Foobar>.
And there is no way that you can use the variable name listItemType as a class name as in List<listItemType>. That's why you were getting "listItemType cannot be resolved to a type". Variable names and type names are in different Java namespaces.
Your attempt <? extends listItemType> fails for the same reason, and
<listItemType.getName()> fails because an expression is not a type.
Another way to think about those failed attempts is that they use runtime values (e.g. the value of listItemType) to do compile time type checking. That implies that the compiler needs to be able to look into the future to see what the runtime values are going to be. Clairvoyance is not a feature of current generation Java compilers :-).
What you need is a generic method.
Generic methods allow type parameters to be used to express
dependencies among the types of one or more arguments to a method
and/or its return type.
This is a full example:
public class NewMain {
private static class Foo {
public Foo() {
}
}
public static void main(String[] args) {
// TODO code application logic here
List<Foo> foos = returnList(Foo.class);
System.out.println(foos.get(0));
}
public static <T> List<T> returnList(Class<T> listItemType) {
List<T> list = new ArrayList<>();
try {
T obj = listItemType.getConstructor().newInstance();
list.add(obj);
} catch (Exception ex) {
Logger.getLogger(NewMain.class.getName()).log(Level.SEVERE, null, ex);
}
return list;
}
}
I have wrote the following code:
public class Test
{
public static void main(String args[]) throws ParseException
{
System.out.println(new Generic<Integer>("one").type); //outputs "one"
}
}
class Generic<T>
{
public T type;
public Generic(Object obj)
{
type = (T)obj;
}
}
And i thought i will get an exception while doing the cast, but i didnt. I get the output: "one". But if i do new generic<Integer>, type become a variable of type Integer, so how can i cast the String "one" to T and store it in the variable type in my generic class without getting an exception? An explanation would be great.
There is no exception because type erasure removes any checking of the Integer type from your code. Since println takes Object the compiler doesn't need to insert a cast, and the code simply erases to:
System.out.println(new Generic("one").type);
Try the following assignment instead:
Integer i = new Generic<Integer>("one").type;
In that case you'll get a ClassCastException because the code erases to:
Integer i = (Integer)new Generic("one").type;
Notice that switching the types behaves differently. This will throw a ClassCastException:
System.out.println(new Generic<String>(123).type);
That's because the println(String) overload is used, so the code erases to:
System.out.println((String)new Generic(123).type);
This is because of java type erasure:
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
basically java will replace T with Object (if T has no bounding upper type like in your example)- and everything is fine at runtime.
It is not casting your string to integer....because it is unbound. It is converting the T to object instead and in the sysout you are getting obj.toString instead.
The proper way would be as below and automatically you will get compile exception in your test class... as expected.
class Generic<T>
{
public T type;
public Generic(T t)
{
type = t;
}
}
class Generic<T>
{
public T type;
public Generic(T obj)
{
type = obj;
}
}
There. Fixed it for you.
This question already has answers here:
Instantiating a generic class in Java [duplicate]
(10 answers)
Closed 9 years ago.
My code is:
public class MyClass<T extends MyComponent> {
private T t;
public MyClass(){
t = new T();
}
}
But the compiler don't accept new T(). Is there a way to do this using the constructor MyClass(), without parameters?
You could do
public class MyClass<T extends MyComponent> {
private T t;
MyClass(Class<T> clazz) throws InstantiationException,
IllegalAccessException {
t = clazz.newInstance();
}
}
Because of type erasure, the JVM doesn't know what T is, so it can't instantiate it. The workaround is to provide a Class<T> and use it to create a new instance:
public MyClass(Class<T> clazz)
{
t = clazz.newInstance();
}
You'll have to add catching IllegalAccessException and InstantiationException.
Not without access to T's class object. Think about it, T could be any subclass of MyComponent, how would you even decide which constructors are available on that particular subclass?
Type erasure means that the compiler silently replaces all "real" references to T with MyComponent.
If you have a reference to Class, you may call Class#newInstance, or get a constructor and invoke that.
I want to use the class information that was captured by the setup of a generic method in Java to potentially create an instance. But I can't see how to reference the class without an actual instance. Is it possible?
public class Fancy {
static public <TypeToFind> TypeToFind createInSomeCase() {
// any type of logic with TypeToFind "class" generic will do, I just want to reference it.
// the below code is invalid, I could also declare a variable, but can't always create an instance to get the class
if (TypeToFind.getClass().equals(Something.class)) {
return TypeToFind.getInstance();
}
}
}
... so later on I could do:
TheResultIsTheParameter t = Fancy.createInSomeCase();
... instead of
TheResultIsAParameter t = Fancy.createInSomeCase(TheResultIsAParameter.class);
... or
TheResultIsAParameter t = Fancy.createInSomeCase(t);
Am I making this too complicated?
You can't do it, because generics are lost at runtime (due to type erasure). You have to pass a Class<?> parameter
Well, you require somethink that is logical, unfortunattelly generics in Java are only a syntactic sugar for reflection.
List<MyClass> list;
(...)
MyClass my = list.get(0);
will compile to
MyClass my = (MyClass) list.get(0);
and this is what will you see in bytecode.
What is more, using reflection or casting to untyped list you can put any object into list and in both codes you'll get ClassCastException.
So the generics exists only on compiler level. A big feature which adds nothing new, only shortens a code in most cases.
As long as you do not try and statically (at compile time) reference any particular class, nothing prevents you from doing something like this:
public class GenericsTest {
#Test
public void testMe() {
GenericsTest test = new GenericsTest();
System.out.println(test.get("Hello").getClass());
}
public GenericsTest() {
super();
}
public <T extends Object> T get(T entity) {
return newInstanceForClass((Class<T>)entity.getClass());
}
public <T extends Object> T newInstanceForClass(Class<T> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
But as you can see, you need to pass in an object of the class you are trying to instantiate, which might not be want you are after. In which case the only other option is to pass in a Class parameterized with the generic type, for reasons that other posters have eloquently stated.