2

I have 3 classes:

class Class1 {}
class Class2 extends Class1 {}
class Class3 extends Class1 {}

I am trying to change the type of array containing Class1 so:

Class1[] arr = ...  // [Class2, Class2, Class3, Class3]
arr = Arrays.copyOf(arr,2)
Class2[] arr2 = (Class2[]) arr

which seems to be impossible yet I can do

Class2[] arr2 = new Class2[arr.length]
for ...
    arr2[i] = (Class2) arr[i]

Is there a way to do this without iterating?

We can assume all the elements in arr (after copyOf) can be typed to Class2.

The error I get is:

CassCastException: [LStuff.Class1; cannot be cast to [LStuff.Class2;

14
  • 1
    By Class2[] arr2 = (Class1[]) arr you probably meant Class2[] arr2 = (Class<<2>>[]) arr - <<...>> was used for readability, not for syntax. Commented Oct 28, 2017 at 18:21
  • 1
    Not every Class1 can be a Class2, right? Commented Oct 28, 2017 at 18:25
  • 1
    Compiler allows us to use Class2[] arr2 = (Class2[]) arr because it is possible that Class1[] arr = ... will be initialized like Class1[] arr = new Class2[size];. If you initialize arr with array of Class2[] type then exception will not be thrown. Otherwise it arr holds array of Class1[] type it can accept instances of Class1, which will cause problems if we will try to use them via Class2[] arr2 like arr2[i].methodAddedInClass2();. Java will not take that risk. Commented Oct 28, 2017 at 18:40
  • 2
    If you have an array that was created by writing new Class1[...], it is simply impossible to change the type to a Class2[]. The ClassCastException is caused by the type of the array, not the elements of the array. It makes no difference that they are all Class2 instances. Commented Oct 28, 2017 at 18:46
  • 1
    @ZergOvermind What Class2[] arr2 = (Class2[]) arr; cares about is type of array held by arr, not type of elements which are placed inside arr because even if at time of casting there ware no elements other than Class2 at the future via arr we can place there any elements of type Class1 and its subclass like Class3. Letting Class2[] arr2 handle Class1[] type array will not be type-safe (which is one of Java's foundations). Commented Oct 28, 2017 at 19:01

5 Answers 5

5

Considering you want to change the type of all the objects and put it to another array where possible; you can use filter along with map. i.e:

Class2[] result = Arrays.stream(arr).filter(e -> e instanceof Class2)
                        .map(e -> (Class2)e).toArray(Class2[]::new);
Sign up to request clarification or add additional context in comments.

11 Comments

stream(Class1) needs to be corrected and how about others that are filtered? Would that require clarity of question?
You might want to relook at the question and maybe explain why shouldn't one cast other instances as the OP is trying to.
@nullpointer as of now, I am basing my answer on the example given, I cannot assume anything more. however, if there are other classes also extending Class1 and OP really wants to use java 8 then they have to filter by multiple criteria which can get messy and then once they get to the map they will need to use a lambda statement rather than a lambda expression and check for the appropriate types prior to casting and so forth... is this what you're asking?
@Aominè my first thought when I saw this, was can you do a method reference on an operator? Class2::intanceOf. I know this will not work now :)
@Eugene: the compiler could simply compile a construct like Class2::instanceOf the same way as Class2.class::isInstance, but I doubt that saving six characters would justify the introduction of another language construct.
|
3

If you know for sure that the first two elements of you array have the desire type, you can request the appropriate array type right when creating the copy using Arrays.copyOf, i.e.

Class1[] arr = ...  // [Class2, Class2, Class3, Class3]
Class2[] arr2 = Arrays.copyOf(arr, 2, Class2[].class);

This is not a Java-8 specific solution, but there is no reason to try to use Java-8 features at all costs. The code above will throw a ClassCastException if your assumption does not hold, but depending on the application logic, this might be better than filtering elements based on their type and silently continuing with different content than expected in the case of an error.

But if you want to filter the elements at runtime, the Stream API does indeed offer the most concise solution:

Class1[] arr = ...  // [Class2, Class2, Class3, Class3]
Class2[] arr2 = Arrays.stream(arr)
   .filter(Class2.class::isInstance).map(Class2.class::cast)
   .toArray(Class2[]::new);

or

Class1[] arr = ...  // [Class2, Class2, Class3, Class3]
Class2[] arr2 = Arrays.stream(arr)
   .flatMap(o -> o instanceof Class2? Stream.of((Class2)o): null)
   .toArray(Class2[]::new);

But note that theses solutions contain more formalism than actually necessary (which is still good for documentation/readability). The toArray method does not require the result array type to be a supertype of the stream’s element type (simply because Java Generics do not support expressing this requirement), therefore, you don’t need the map step:

Class2[] arr2 = Arrays.stream(arr).filter(Class2.class::isInstance).toArray(Class2[]::new);

which is the most concise solution with filtering.

Comments

1

You can not cast base type to its child, otherwise you will get exception. Doing this:

Class2[] arr2 = new Class2[arr.length]
for ...
    arr2[i] = (Class2) arr[i] // arr might contain different type of derrived class

is the same as casting class1 (base) type to class2 (child). So, I think that the context of the problem might be wrong.

Comments

1

First here : Class1[] arr = ...

Elements of the arr are declared with the Class1 type.
It means that these elements are not necessarily Class2 instances.
So you may have runtime error during the cast of the array if arr doesn't refer to a Class2[] object.
For example, this may create this kind of issue :

Class1[] arr = new Class1[5];
arr[0] = new Class1();
Class2[] arr2 = (Class2[]) arr; // exception at runtime  [LClass1; cannot be cast to [LClass2;

You should use an instanceof check rather than casting directly such as :

Class2[] arr2 = (Class2[]) arr;

With Java 8, you could use filter() to make the instanceof check and combined it to toArray() to create a array of Class2[] :

   Class1[] arr = new Class1[]{new Class1(), new Class2()};
   Class2[] arr2 = Arrays.stream(arr)
            .filter(e -> e instanceof Class2)
            .toArray(Class2[]::new);

As result you will have only the Class2 instance in arr2.

1 Comment

They might not neccesary be but they ARE that type because i made sure to clear all of the other types away, i just dont wanna go through loops and copy into new array. I have an array in which i organize fitting types, then cut them off with copyOf and i want to change the type into Class2 so i can return it. But for some reason i get an error, eventhe debugger confirms i have no other classess but Class2 in there yet it runs into the error.
0

Yeah if you use java8 you can use lambdas to iterate and cast in a one-liner. For older versions you can also use Arrays.copyOf(...).

Here is the result of a similar question: casting Object array to Integer array error

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.