How to initialize array in Generics with mix data types

AHF :

I am going through the Java Arrays and additionally, I am looking into generics. Below are the two methods of initializing an array

int[] data = {1,2,3,4,5,6,7,8,9};

// or

int[] data;
data = new int[] {1,2,3,4,5,6,7,8,9};

But when I am using generics, I have mixture of data types for example,

String[] outcome = {"0 wins", "Tie" , "X wins"};

Above array has a single data type of String. What if in an array I have something like below

outcome = {7, "Tie" , 9.0};

now I have a mixture of data types in an array. How I can write it or if it is possible to do with generics? Can I do it with ArrayList?

Thank you

Turing85 :

tl;dr: In my opinion, arrays are not a good fit for the problem, you should use objects instead.


This is not a direct answer to your question, but an answer in the form of a redesign.

First of, let us tackle your statement about generics and arrays. Arrays are covariant and retained, while generics are invariant and erased.

  • Covariant means that when B extends A, you can Write A[] aArray = new B[someSize];. Invariant means that this is not possible: ArrayList<A> aList = new ArrayList<B>(); will lead to a compile time error.
  • Retained means that the information about the type is retained at runtime: an array always "knows* what type its elements has. Erased means that the type information is gone after compilation. This is also called Type Erasure.

The mixture of covaraint and retained vs. invariant and erased has good potential to get you into trouble. That is the reason why ArrayList uses an Object[] instead of a T[] as its backing datastructure.


Now to the actual question. As was already said by others, we could go down the road ande create an Object[]. I would strongly advice against this since we loose all type information. The only way to get back that information is a instanceof check, which makes your code rigid. Imagine you change the type of an entry. In this case, the instanceof will return false, possibly leading to unwanted behavior and (best case) some test turning red or (worst case) we may not notice it.

Now how to get around this? We create a class representing (what I infer are) match results:

public class MatchResult {
    private final int firstTeamScore;
    private final int secondTeamScore;

    public MatchResult(final int firstTeamScore, final int secondTeamScore) {
        this.firstTeamScore = firstTeamScore;
        this.secondTeamScore = secondTeamScore;
    }

    public int getFirstTeamScore() {
        return firstTeamScore;
    }

    public int getSecondTeamScore() {
        return secondTeamScore;
    }

    public String getResultForFirstTeam() {
        if (firstTeamScore > secondTeamScore) {
            return "Win"; // In an actual implementation, I would replace this with an enum
        } else if(firstTeamScore = secondTeamScore) {
            return "Tie";
        } else {
           return "Lose";
        }
    }
    // You can add a method public String getResultForSecondTeam(), I omitted it for brevity
}

What have we won? We have types. The scores are always ints, the results always Strings. If we were, for example, change the type of the getReultforFirstTeam() from String to, e.g., an Enum, we would get compiler errors for all locations where the type does not match anymore. Thus, we hone the fail-fast design and are forced to modify the code where necessary. And thus, we do not even have the chance to get the sneaky, unwanted behaviour we had before.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=141966&siteId=1