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.
Class2[] arr2 = (Class1[]) arryou probably meantClass2[] arr2 = (Class<<2>>[]) arr-<<...>>was used for readability, not for syntax.Class1can be aClass2, right?Class2[] arr2 = (Class2[]) arrbecause it is possible thatClass1[] arr = ...will be initialized likeClass1[] arr = new Class2[size];. If you initializearrwith array ofClass2[]type then exception will not be thrown. Otherwise itarrholds array ofClass1[]type it can accept instances ofClass1, which will cause problems if we will try to use them viaClass2[] arr2likearr2[i].methodAddedInClass2();. Java will not take that risk.Class2[] arr2 = (Class2[]) arr;cares about is type of array held byarr, not type of elements which are placed insidearrbecause even if at time of casting there ware no elements other thanClass2at the future viaarrwe can place there any elements of type Class1 and its subclass likeClass3. LettingClass2[] arr2handleClass1[]type array will not be type-safe (which is one of Java's foundations).