Max M Max M - 11 months ago 60
Java Question

How to cast an java Object to its own getComponentType?

I've a Class containing a private list of objects.

private List<Object> mylist;


Additionally my class contains two methods:

(1) addObject

public void addObject(Object obj)
{
this.mylist.add(obj);
}


(2) dropObject

dropObject is a bit tricky. I want do drop the first element from
mylist
that is equal (but not the same) to the parameter
obj
. Therefore I compare the class of the parameter
obj
to every element's class in
mylist
. If the class of the current element matches, I want to compare, whether both elements are equal (but not the same).

Here I've got some problem to compare arrays with each other. I'd like to use
Array.deepEquals()
which requires a typecast for both
obj
and
curr_obj
.

So here's my code:

public void dropArgument(Object obj) {

if (obj == null) {
return;
}

Object objRemove = null;

for (Object curr_obj : this.mylist) {

if (curr_obj.getClass() != obj.getClass()) {
continue;
}

// primitive data type comparison
if (obj.getClass().isPrimitive() && curr_obj == obj) {
objRemove = curr_obj;
}
// array comparison
else if ((obj.getClass().isArray())
/* the following line gives me headache */
&& (Arrays.deepEquals((Object[]) curr_obj, (Object[]) obj))) {
objRemove = curr_obj;
}
// wrapper / collection comparison
else if (curr_obj.equals(obj)) {
objRemove = curr_obj;
}
// comparison of any other classes which are assumed not to have an 'equals' method.
else {
Field[] fInputFields = obj.getClass().getDeclaredFields();
Field[] fFields = curr_obj.getClass().getDeclaredFields();
if (Arrays.deepEquals(fInputFields, fFields)) {
objRemove = curr_obj;
}
}
}

// delete obj match if found
if (objRemove != null) {
this.mylist.remove(objRemove);
}
}


I wrote tests for this method and every type of array get's the following (stackstrace of an int[] array):

error:
java.lang.ClassCastException: [I cannot be cast to [Ljava.lang.Object;
at javafxTablePane.FieldMethodData.dropArgument(FieldMethodData.java:119)
at tests.DropArgumentsTests.test_dropMultiArrayInteger(DropArgumentsTests.java:345)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)


The error is clear:
int[]
can't be typecasted to
Object[]
. So my question is, as
Array.deepEquals()
requires a typecast, how can I re-cast my objects to their own class/data type as array?

I searched for this and found the
getClass().getComponentType()
method with delivers the array type of my object. But I don't know (if even possible) how to use that to typecast
obj
to its own array-type.

Some additional infos:


  1. I want to use
    Object obj
    as parameter for my method to be able to get any object independent of class or data type.

  2. I am aware, that I could simply overload my method to handle each data type itself. But I try to solve this generic.



Update



I've followed Darshan Mehta's suggestion and implemented a simple method that converts 1 dimensional arrays from primitive to wrapper classes. I can continue developing more generic code (e.g. casting arrays of any dimension) with that, so this solution fits my issue. I have put my code here for those who are interested.

/**
* <b>ArrayEquals</b>
* <p>
* Compares two arrays of any same class and delivers their equality as boolean.
* </p>
*
* @param obj1
* [Object] : any object of any class as Object
* @param obj2
* [Object] : any object of the same class as obj1 as Object
* @return [boolean] : Equality of obj1 and obj2
*/
private boolean ArrayEquals(Object obj1, Object obj2) {

Class<?> cObj1 = obj1.getClass().getComponentType();

// convert byte[] to Byte[]
if (cObj1.equals(byte.class)) {

byte[] tmpObj1 = (byte[]) obj1;
byte[] tmpObj2 = (byte[]) obj2;
if (tmpObj1.length != tmpObj2.length) {
return false;
}
Byte[] newObj1 = new Byte[tmpObj1.length];
Byte[] newObj2 = new Byte[tmpObj2.length];

for (int i = 0; i < tmpObj1.length; i++) {
newObj1[i] = tmpObj1[i]; // Autoboxing
newObj2[i] = tmpObj2[i]; // Autoboxing
}
return Arrays.deepEquals(newObj1, newObj2);
}
// convert short[] to Short[]
else if (cObj1.equals(short.class)) {
short[] tmpObj1 = (short[]) obj1;
short[] tmpObj2 = (short[]) obj2;
if (tmpObj1.length != tmpObj2.length) {
return false;
}
Short[] newObj1 = new Short[tmpObj1.length];
Short[] newObj2 = new Short[tmpObj2.length];

for (int i = 0; i < tmpObj1.length; i++) {
newObj1[i] = tmpObj1[i]; // Autoboxing
newObj2[i] = tmpObj2[i]; // Autoboxing
}

return Arrays.deepEquals(newObj1, newObj2);
}
// convert int[] to Integer[]
else if (cObj1.equals(int.class)) {
Integer[] newObj1 = Arrays.stream((int[]) obj1).boxed().toArray(Integer[]::new);
Integer[] newObj2 = Arrays.stream((int[]) obj2).boxed().toArray(Integer[]::new);
return Arrays.deepEquals(newObj1, newObj2);
}
// convert long[] to Long[]
else if (cObj1.equals(long.class)) {
Long[] newObj1 = Arrays.stream((long[]) obj1).boxed().toArray(Long[]::new);
Long[] newObj2 = Arrays.stream((long[]) obj2).boxed().toArray(Long[]::new);
return Arrays.deepEquals(newObj1, newObj2);
}
// convert float[] to Float[]
else if (cObj1.equals(float.class)) {
float[] tmpObj1 = (float[]) obj1;
float[] tmpObj2 = (float[]) obj2;
if (tmpObj1.length != tmpObj2.length) {
return false;
}
Float[] newObj1 = new Float[tmpObj1.length];
Float[] newObj2 = new Float[tmpObj2.length];

for (int i = 0; i < tmpObj1.length; i++) {
newObj1[i] = tmpObj1[i]; // Autoboxing
newObj2[i] = tmpObj2[i]; // Autoboxing
}

return Arrays.deepEquals(newObj1, newObj2);
}
// convert double[] to Double[]
else if (cObj1.equals(double.class)) {
double[] tmpObj1 = (double[]) obj1;
double[] tmpObj2 = (double[]) obj2;
if (tmpObj1.length != tmpObj2.length) {
return false;
}
Double[] newObj1 = new Double[tmpObj1.length];
Double[] newObj2 = new Double[tmpObj2.length];

for (int i = 0; i < tmpObj1.length; i++) {
newObj1[i] = tmpObj1[i]; // Autoboxing
newObj2[i] = tmpObj2[i]; // Autoboxing
}

return Arrays.deepEquals(newObj1, newObj2);
}
// convert boolean[] to Boolean[]
else if (cObj1.equals(boolean.class)) {
boolean[] tmpObj1 = (boolean[]) obj1;
boolean[] tmpObj2 = (boolean[]) obj2;
if (tmpObj1.length != tmpObj2.length) {
return false;
}
Boolean[] newObj1 = new Boolean[tmpObj1.length];
Boolean[] newObj2 = new Boolean[tmpObj2.length];

for (int i = 0; i < tmpObj1.length; i++) {
newObj1[i] = tmpObj1[i]; // Autoboxing
newObj2[i] = tmpObj2[i]; // Autoboxing
}

return Arrays.deepEquals(newObj1, newObj2);
}
// convert char[] to Character[]
else if (cObj1.equals(char.class)) {
char[] tmpObj1 = (char[]) obj1;
char[] tmpObj2 = (char[]) obj2;
if (tmpObj1.length != tmpObj2.length) {
return false;
}
Character[] newObj1 = new Character[tmpObj1.length];
Character[] newObj2 = new Character[tmpObj2.length];

for (int i = 0; i < tmpObj1.length; i++) {
newObj1[i] = tmpObj1[i]; // Autoboxing
newObj2[i] = tmpObj2[i]; // Autoboxing
}
return Arrays.deepEquals(newObj1, newObj2);
}
// is no primitive
else {
return Arrays.deepEquals((Object[]) obj1, (Object[]) obj2);
}
}

Answer Source

As you are comparing two objects, you should do the following:

  • Extract out the comparison logic into a new method (e.g. private boolean equals(Object o1, Object o2) and call it from your dropObject method).
  • For Array type, get component type using array.getClass().getComponentType() method call and try to cast it in an appropriate array. Casting an int array to Object array will throw ClassCastException. Refer to this SO answer for more explanation on casting and array type.
  • If a component type is an array, implement the logic to iterate over all the elements and call equals method for each element.
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download