4

Some context: I'm parsing an accounting ledger which has account1 and account2 as int types. Each is a number in the range [0, 99999]. I have many combinations to consider.

Ideally I'd like to use something like

switch (account1, account2){
    case (1,1):
        /*account1 is 1, account2 is 1*/
    case (2,1):
        /*account1 is 2, account2 is 1*/
}

and so on. (I only need to consider about 20 possible combinations).

Is there a way I can achieve this in Java?

I've considered this question Storing number pairs in java

and could build an IntPair class. Perhaps if I define bool equals then I could switch on an instance, in a similar manner in which you can switch on java.lang.String.

Community
  • 1
  • 1
Michael Bullock
  • 976
  • 6
  • 13
  • 3
    Not possible. Switch use a limit amout of type (primitive and String). And only a constant value can be set in the case field. But an `if-else` would suit you if you were ready to use a switch – AxelH Jan 24 '17 at 14:46
  • 1
    What is your real purpose of that? Are you trying to check different combinations? Maybe an inner loop is useful for you with an if-else statment inside. – Braisly Jan 24 '17 at 14:48
  • Alas I need to respond pretty much uniquely to each particular accounting code combination. – Michael Bullock Jan 24 '17 at 14:49
  • 1
    @MichaelBullock and you want to hard code every one of the 100000 times 100000 combinations??? – user85421 Jan 24 '17 at 14:51
  • 1
    If every combination have his own behavior, I suggest you to rethink everything. – AxelH Jan 24 '17 at 14:56
  • What changes with different combinations? Do you set different values to the same variables? Do you call different methods? – biziclop Jan 24 '17 at 14:56
  • No I can ignore most of the theoretical combinations. I only need to worry about 20 of them. – Michael Bullock Jan 24 '17 at 14:56
  • For each particular case I might call a function which has different parameters to a function called from another case. – Michael Bullock Jan 24 '17 at 14:57
  • @MichaelBullock That smells really bad. I would probably separate this into two methods for sanity: one would take the account numbers and would just return an enum. The second part would take the enum value and do a simple switch/case on it to decide which function to call and how. – biziclop Jan 24 '17 at 15:00

4 Answers4

9

Alas this is not possible in Java. You can only switch on integral types, or a java.lang.String from Java version 7. (You can't build this functionality into your own type by overriding equals and hashCode from java.lang.Object).

But that could inspire you to make a hack:

switch (Integer.toString(account1) + "_" + Integer.toString(account2)){
    case "1_1":
    case "2_1":    
}

I find this to be surprisingly readable.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 1
    switch String is from java 7 I think – AxelH Jan 24 '17 at 14:53
  • 2
    You are welcome. Nice hack by the way ;) That is still a lot of combinaision – AxelH Jan 24 '17 at 14:54
  • OP claims there are only 20. Good luck to the chap. – Bathsheba Jan 24 '17 at 14:55
  • Yep, in the comment his state _Alas I need to respond pretty much uniquely to each particular accounting code combination_ so I am a bit concerned about this ;) – AxelH Jan 24 '17 at 14:57
  • for readability I would use something like `switch (String.format("%05s_%05s", account1, account2)) {` – user85421 Jan 24 '17 at 15:01
  • @CarlosHeuberger: Yes that's pretty. But a stone's throw away from the dreaded octal literal pitfall. – Bathsheba Jan 24 '17 at 15:02
  • but more accounting like.... and "_" is also special in numbers.... (such a déjà vu: http://stackoverflow.com/a/8851333/85421) [:-) – user85421 Jan 24 '17 at 15:04
  • This is a nice hack, but it reduces compiler awareness and is not refactor-safe. Need to take that into the equation when choosing to use it. – Pedro Borges Dec 02 '20 at 10:30
4

EDIT :

Maybe this ?

public enum Account{

    ACC_512_401(512,401), 
    ACC_512_402(512,402);

    private final int accA;
    private final int accB;

    Account(int accA, int accB){
        this.accA=accA;
        this.accB=accB;
    }

    private int getAccA(){
        return accA;
    }
    private int getAccB(){
        return accB;
    }
    public static Account getEnum(int accA, int accB){
        for(Account acc : values()){
            if(accA == acc.getAccA() && accB == acc.getAccB()){
                return acc;
            }
        }
        return null;
    }
}



public class testswitch {
    public static void main(String[] args) {
        test(Account.getEnum(512, 401));
    }

    public static void test(Account acc){

        switch (acc){
        case ACC_512_401:
            System.out.println("A");
            break;
        case ACC_512_402:
            System.out.println("A");
            break;
            default:
                break;
        }
    }
}

You do your switch over your enum.

EDIT : I added getEnum method to get value for int input. Now it's what you want I guess.

Morgan
  • 124
  • 4
  • Upvoted. This is quite nice, as it makes things more type safe, if you get my meaning. Although perhaps it's not quite so readable at the `case` point. – Bathsheba Jan 24 '17 at 15:01
  • Why did I forgot the `enum`s ... nice solution !. @Bathsheba If the enums are more like `A1_1` like your concatenation idea, this will still be acceptable – AxelH Jan 24 '17 at 15:02
  • 1
    I prefer this way to my answer. I think you should amend it to include the possibility of pretty labels. – Bathsheba Jan 24 '17 at 15:03
  • nice, but missing how to get the value for the enum, given the two accounts – user85421 Jan 24 '17 at 15:06
0

One possible strategy is like this, a slight variation on @Morgan's answer:

public enum AccountRelationship {
    BOSS_ACCOUNTS,
    SECRET_SWISS_TAX_AVOIDING_ACCOUNTS,
    OTHER;
}

public static final Map<AccountPair,AccountRelationship> relationships = new HashMap<>();
static {
    relationships.put(new AccountPair(1,1),AccountRelationship.BOSS_ACCOUNTS);
    relationships.put(new AccountPair(1,5),AccountRelationship.BOSS_ACCOUNTS);
    relationships.put(new AccountPair(2,2),AccountRelationship.SECRET_SWISS_TAX_AVOIDING_ACCOUNTS);
}

public void doSomething(AccountPair pair) {
    AccountRelationship rel = relationships.getOrDefault(pair, AccountRelationship.OTHER);
    switch (rel) {
    case BOSS_ACCOUNTS: receiveLotsOfBonus(); break;
    case SECRET_SWISS_TAX_AVOIDING_ACCOUNTS: laughAllTheWayToTheBank(); break;
    case OTHER: payLotsOfTaxes();
    }
}

This separates the logic of determining what to do from calling the actions, making things a lot more readable and easier to extend. Not to mention that you can easily unit test everything separately.

As you can see, it's pretty verbose though.

It's got one other potential pitfall: the relationships map. This is great if you have several account pairs that require the same action, but when you add a new value to AccountRelationship, it's easy to forget to update this map too.

biziclop
  • 48,926
  • 12
  • 77
  • 104
0

Just for the fun of using primes: (it's safe in the mentioned range, but on high integers it will surely fail)

The case calculation will be done on compile-time.

public class Switch {

    public static void main(String[] args) {
        int i = 1;
        int j = 2;
        switch (i * 2 + j * 3) {
        case 1 * 2 + 2 * 3:
            System.out.println("match");
        }
    }
}
Turo
  • 4,724
  • 2
  • 14
  • 27