I'm getting a ClassCastException in the follow code:
Destination[] destinations;
ArrayList<Destination> destinationsList = new ArrayList<Destination>();
// .....
destinations = (Destination[]) destinationsList.toArray();
My Destination class looks like this:
public class Destination {
private String code;
Destination (String code) {
this.code = code;
}
public String getCode () {
return code;
}
}
Syntactically I'm not getting any errors, this only occurs at run time. It's confuzing though because aren't all classes essentially derivatives of the Object class? And if so, why does this cast conversion error even occur?
toArray() returns an Object[]. What you need is toArray(T[] a) because of the type erasure, a generic collection cannot create a typed array.
By using the overloaded method, you can help it to create a typed array of Destination objects.
Use
destinations = destinationsList.toArray(new Destination[destinationList.size()]);
because toArray returns an object array not your Destination[]
replace it with this
destinations[] = destinationsList.toArray(new Destination[destinationList.size()]);
this would populate the new Destination Array object and return the populated array.
Edit:
To answer your question in comment in #ZouZou's answer.
you need the new Destination[] because a Destination[] can be referred by a Object[] but the other way round is not possible.
to clarify things,
String s = "hello";
Object o = s;
s = (String) o; //works
//but
String s = "hello";
Object o = s;
o = new Object;
s = (String) o; //gives you a ClassCastException because an Object
//cannot be referred by a string
Because a String has all the properties defined in the Object class through inheritence but an Object doesn't possess the properties of a String object. That is why it is legal to cast up the inheritence tree and downcasting is not.
It is not implemented on the language level because of how generics are put in the language. Also do not try something like this :
// Destination[] destinations;
ArrayList<Destination> destinationsList = new ArrayList<Destination>();
//add some destinations
destinationsList.add(new Destination("1"));
destinationsList.add(new Destination("2"));
// .....
Object[] destinations = destinationsList.toArray();
destinations[1] = "2"; //simulate switching of one object in the converted array with object that is of other type then Destination
for (Object object : destinations) {
//we want to do something with Destionations
Destination destination = (Destination) object;
System.out.println(destination.getCode()); //exception thrown when second memeber of the array is processed
}
Use this :
destinations = destinationsList.toArray(new Destination[0]); //yes use 0
Related
This is my sample class
public class Value<E> {
public final E value;
public Value(E value) {
this.value = value;
}
}
And this returns a String and no Object, so I don't need to cast it.
String a = new Value<String>("test").value;
However, if I want to do this, then I need to cast it.
ArrayList<Value<?>> a = new ArrayList<>();
a.add(new Value<String>("test"));
String b = a.get(0).value; // Runtime error
If I add the value to the ArrayList it returns an Object, but I want it to return what I defined the Element to be.
How can I solve this?
Replace
ArrayList<Value<?>> a = new ArrayList<>();
with
ArrayList<Value<String>> a = new ArrayList<>();
You can't. You defined that you don't know (or care) what types are contained inside with Value<?>, so you can only treat them as Object as that's the one thing they're guaranteed to be (they can also be subclasses, but they'll still always be Objects too).
If you think and hope you can store Strings and Integers in the same List, that's not possible except by treating them as Objects. Besides, you shouldn't be storing different types (except through inheritance hierarchy) in the same collection anyway.
I know that in Java due to inheritance, a superclass type variable can hold a reference to a subclass object. Example, say ClassA is the superclass to ClassB. You could set an element of a ClassA[] to reference a ClassB object. I'm running into a problem doing this while using a copy constructor of the subclass.
public Items copyFromMasterInventory(String str){
Items itemToReturn = null;
int length = masterInventory.length;
int iterator = 0;
while (iterator < length){
String name = masterInventory[iterator].getName();
if (name.equals(str)){
if (name.getClass().equals(Collectible.class)){
itemToReturn = new Collectible(masterInventory[iterator]);
}
if (name.getClass().equals(Props.class)){
itemToReturn = new Props(masterInventory[iterator]);
}
}
}
return itemToReturn;
}
In this case, there are three class. Items, Props, and Collectible. Props and Collectible are subclasses of Items. masterInventory is an array of Items storing both Collectible and Props objects. The purpose of this method is to create and return a copy of an item in the masterInventory array. To avoid .clone(), I've created copy constructors for Collectible and Props. But the way I have it now shows an incompatible types error, that Items cannot be converted to Collectible or Props, even though the object stored in that element is a Collectible or Props object. If been reading and searching for hours but can't seem to come up with a solution. Does anyone know a way around this issue? All help is appreciated.
You could add an abstract method Item getCopy() to the Item class, implement it in both Props and Collectible, and call it in de while loop:
itemToReturn = masterInventory[iterator].getCopy();
the benefit here is that you do not need the condition on class.
The solution is simple - casting :
if (masterInventory[iterator] instanceof Collectible) {
itemToReturn = new Collectible((Collectible) masterInventory[iterator]);
}
if (masterInventory[iterator] instanceof Props) {
itemToReturn = new Props((Props) masterInventory[iterator]);
}
I am working over Java collections.
I found something strange and not getting why is this happening.
So here is the scenario.
ArrayList al = new ArrayList();
al.add("Hello World");
//The below line is creating some confusion
System.out.print(al.get(0));
Now the last line is printing "Hello World" as it is.
But as i am not using any type parameter it should give an object type in return.So it should call the Object's toString() instead of String's toString().
Does it depend on the object type which is getting saved (like String here ) of the return type (like Object).
Please help.
Actually all the trick is not related to generics even you snippet showcase it.
It related to overriden methods, toString() is defined in the super super class Object and overriden in the String class, that are evalutated at runtime using the actual object type and not its reference, i.e when calling:
Object o = new String("hello");
o.toString(); // Here at runtime, the JVM will call the "hello" toString method and not the Object one.
In you code, you have called:
al.add("Hello World");
The above line do the following:
Creates a new String object with Hello World as a literal and add it to the string pool.
Makes the first element in the list (element at index 0) reference that object (that is a String one).
Now when calling System.out.print(al.get(0));:
The first element of the list, which is an Object reference to a String object, got his toString() method called and which as already said will evalute to the object real type and not the refence type, i.e. you will have String#toString method called.
If you want to return Strings from a collection you'll have to initialise it like this, using a String type parameter:
ArrayList<String> al = new ArrayList<String>();
// ^^^^^^^^ ^^^^^^^^
al.add("Hello World");
String value = al.get(0); //<-- now you can get Strings
Now, however you will only be able to get objects, and you will have to cast before getting back to your original type:
ArrayList al = new ArrayList();
al.add("Hello World");
Object object = al.get(0); //<-- ok
String value = (String)al.get(0); // <-- ok
value = al.get(0); //<-- wont compile
Edit
ArrayList al = new ArrayList();
al.add("Hello World");
Object object = al.get(0); //<-- ok
String value = object.toString();
In this case, calling object.toString(), will just return the object as a String. It will call the toString method on the String class, which just returns itself.
When calling a method of an object, the actual method that is called is of the actual type of the object. This is why the toString that is actually called is the String class method.
BTW: generic types are for compile-time, and not for runtime. For example you can't have in the same class 2 methods like:
void foo(List l) and
void foo(List l) since after compiling both have the same signature.
I using type object and I fill it with data inside loop and at the end I want to clear the data inside ,I don't see any option to clear it (with .+CTRL SPACE) and I don't want to create new instance for it because I want to create object type just once ,is there a workaround to clear it?
I want to do the following just once for specObject, i.e. create instance type list or object and than I have loop that I fill the data inside this object ,when i finish and want to create new instance in specObject I want to clear it before ,how should I do that?
List<Object> itemObject = null;
Object specObject = null;
// Check instance type whether is list or specific object instance
if (multiplicity.equals(MULTI_N)) {
itemObject = new ArrayList<Object>();
return itemObject;
} else if (multiplicity.equals(MULTI_1)) {
return specObject;
} else {
return specObject;
}
You can call the clear method on the List object. That will remove all elements without the need to create a new instance. The documentation is here.
Just to note on object references
when i finish and want to create new instance in specObject I want to clear it before ,how should I do that?
Let's say you have a list:
ArrayList<String> strings = new ArrayList<String>();
If you add some string objects to this list:
strings.add("Hello");
strings.add("There");
strings.add("StackOverflow");
Then you nullify the strings object.
strings = null;
You have effectively removed all of the elements inside the list? Why? Well when you declare ArrayList<String> strings;, you're not creating a new object. You're creating a new reference (pointer) to an object. To illustrate this:
String s = "Hello";
String s2 = s; // s2 points to the same object that s points to.
String s3 = "Another String"; // S3 points to a different object.
The one exception to this rule is if you declare:
String s = "Hello";
String s2 = "Hello"; // s2 will point to the same object as s.
When an object isn't pointed to by anything, it is removed by the Garbage Collector. So effectively, if you declare:
strings = null;
You're removing all of the String child objects that you added to.
I'm trying to create a class that can instantiate arrays at runtime by giving each array a "name" created by the createtempobjectname() method. I'm having trouble making this program run. I would also like to see how I could access specific objects that were created during runtime and accessing those arrays by either changing value or accessing them. This is my mess so far, which compiles but gets a runtime exception.
import java.lang.reflect.Array;
public class arrays
{
private static String temp;
public static int name = 0;
public static Object o;
public static Class c;
public static void main(String... args)
{
assignobjectname();
//getclassname();//this is supposed to get the name of the object and somehow
//allow the arrays to become updated using more code?
}
public static void getclassname()
{
String s = c.getName();
System.out.println(s);
}
public static void assignobjectname()//this creates the object by the name returned
{ //createtempobjectname()
try
{
String object = createtempobjectname();
c = Class.forName(object);
o = Array.newInstance(c, 20);
}
catch (ClassNotFoundException exception)
{
exception.printStackTrace();
}
}
public static String createtempobjectname()
{
name++;
temp = Integer.toString(name);
return temp;
}
}
Create a Map then you can add key/value pairs when the key is your name and the value is your array.
Following up from #Ash's answer, here is some illustrative code. Notice that there is no reflection involved.
Map<String, Object> myMap = new HashMap<String, Object>();
...
Object myObject = ...
myMap.put("albert", myObject); // record something with name "albert"
...
Object someObject = myMap.get("albert"); // get the object named "albert"
// get("albert") would return null if there nothing with name "albert"
EDIT I've edited the example to use the type Object, since that is more closely aligned with what you are trying to do (I think). But you could use any type instead of Object ... just replace the type throughout the example. And you can do the same with an ArrayList; for example:
List<Date> dates = new ArrayList<Date>();
dates.add(new Date());
Date firstDate = dates.get(0);
Notice that no typecasts are required.
I expect you're getting a ClassNotFoundException from this line:
c = Class.forName(object);
The value of object the first time it's called is "1", which is not a valid class name.
Class.forName requires a class name as input, such as "java.lang.Integer". Trying to "name" your array in this way doesn't make sense to me. You need to pick an appropriate Java class name.
If you want to "name" an array instance (after you've created it), you could always store the instance as the value in a Map, using the name as the key.