Table of contents
Definition
Covariant arrays mean an array of type T can hold elements of type T and the subtype of T.
public class ArraysCovariance { public static void main(String[] args) { Number[] numbers = new Number[3]; Number num = 100; numbers[0] = 10; numbers[1] = 10.5; numbers[2] = 12l; numbers[3] = num; for (Number number : numbers) { System.out.println("Element is : "+number); } } } Output : Element is : 10 Element is : 10.5 Element is : 12 Element is : 100 Note* Here Array of type Number is holding Number datatype as well as subtype of Number ( Integer , Double , Long )
Array Subtyping
The subtyping rule says if S is a subtype of T then S[] is also a subtype of T[].
public class ArraysCovariance1 { public static void main(String[] args) { Number[] numbers = new Integer[2]; numbers[0] = 10; numbers[1] = 12; for (Number number : numbers) { System.out.println("Element is : "+number); } } } Output : Element is : 10 Element is : 12 Note* Integer is a subtype of Number hence Number[] is used as a reference for Integer[]
If S is not a subtype of T then S[] is not a subtype of T[]. Java throws a type mismatch compile-time exception if we try to assign S[] to T[].
public class ArraysCovariance1 { public static void main(String[] args) { Number[] numbers = new String[2]; // Compilation Error numbers[0] = 10; numbers[1] = 12; for (Number number : numbers) { System.out.println("Element is : "+number); } } }
CE : Exception in thread “main” java.lang.Error: Unresolved compilation problem: Type mismatch: cannot convert from String[] to Number[]
at ArraysCovariance1.main(ArraysCovariance1.java)
Passing subtype array to supertype array
Consider a scenario when an Integer[] is created but a specific method requires a Number[] as an argument. So can we pass Integer[] to Number[]? Yes, this is possible with the help of array subtyping. As mentioned earlier if S is a subtype of T then S[] is also a subtype of T[].
public class ArraysCovariance2 { public static void main(String[] args) { Integer[] integers = new Integer[3]; integers[0] = 10; integers[1] = 11; integers[2] = 12; printNumbers(integers); } static void printNumbers(Number[] numbers) { for (Number number : numbers) { System.out.println("Element is : "+number); } } } Output : Element is : 10 Element is : 11 Element is : 12
Refiable type
Consider a scenario when we pass an Integer[] to a method that accepts Number[] and we manipulate the array in that method. Check the example below :
public class ArrayCovariance3 { public static void main(String[] args) { Integer[] integers = new Integer[4]; integers[0] = 10; integers[1] = 11; integers[2] = 12; printNumbers(integers); } static void printNumbers(Number[] numbers) { numbers[3] = 12L; // Runtime Exception for (Number number : numbers) { System.out.println("Element is : "+number); } } }
Output : Exception in thread “main” java.lang.ArrayStoreException: java.lang.Long at ArrayCovariance3.printNumbers(ArrayCovariance3.java) at
ArrayCovariance3.main(ArrayCovariance3.java)
The above code will throw a runtime exception because we can fool the compiler but we cannot fool the Java runtime environment. JRE will know the array was instantiated with Integer[] but now we are trying to add a Long data type to Integer[] in disguise of a Number[]. Hence java throws a runtime exception. So the array is termed a Refiable type
If you like this covariant array article please share it. If you want to add anything extra please comment below and write to me if I have gone wrong anywhere.