ForInfinity ForInfinity - 1 month ago 12
Java Question

Comparing and sorting parametrized types in a list?

I am experimenting with a simple program that creates a bank account and has transactions tied to it. It works flawlessly when I am using primitive types, however I included a possibility of adding a String operation (such as ERROR when withdrawing), and now I am not sure how can I sort the list so that the String operations don't appear at all when printed out, if filtered, or appear at the bottom if sorted.

Full source code:

public static void main(String[] args) {
BankAccount bankAccount = new BankAccount();
bankAccount.addTransaction(2, TransactionType.DEPOSIT);
bankAccount.addTransaction(100.66, TransactionType.DEPOSIT);
bankAccount.addTransaction(2, TransactionType.WITHDRAW);
bankAccount.addTransaction("ERROR", TransactionType.WITHDRAW);

List<Transaction> transactions = bankAccount.getTransactions();

List<Transaction> collect = transactions.stream()
//Error appears over here, probably due to wrong syntax of the two next lines.
.sorted(Comparator.comparing((Transaction tr) -> tr.getAmount()).reversed())
.filter(tr -> tr.getAmount() > 0)
.collect(toList());
collect.forEach(tr -> System.out.println(tr));
}

private static class BankAccount {
private List<Transaction> transactions = new ArrayList<>();

public <T> void addTransaction(T amount, TransactionType transactionType) {
Transaction transaction = new Transaction(amount, transactionType);
transactions.add(transaction);
//return 0;
}

public List<Transaction> getTransactions() {
return Collections.unmodifiableList(transactions);
}

@Override
public String toString() {
return "BankAccount{" +
"transactions=" + transactions +
'}';
}
}

private static class Transaction<T> {
private final T amount;
private final TransactionType transactionType;
private final Date dateCreated;

public Transaction(T amount, TransactionType transactionType) {
this.amount = amount;
this.transactionType = transactionType;
this.dateCreated = new Date();
}

public T getAmount() {
return amount;

}

public TransactionType getTransactionType() {
return transactionType;
}

public Date getDateCreated() {
return dateCreated;
}

@Override
public String toString() {
return "Transaction{" +
"amount=" + amount +
", transactionType=" + transactionType +
", dateCreated=" + dateCreated +
'}';
}
}

private static enum TransactionType {
DEPOSIT, WITHDRAW;
}


}

Answer

Your issue is you are comparing apples to oranges when you mix Transaction<Double> with Transaction<String>. Your BankAccount can only hold one type of Transaction. They are either all Transaction<Double> or Transaction<String>.

Right now because you do not have your BankAccount class parameterized it is treating it like Transaction<Object>.

The solution is to properly parameterize the BankAccount and code accordingly. Here is a version coded up using BankAccount<String>.

NOTE: The example is relying on String comparison of numbers which is not a sound strategy. I moved the filter() call up to remove the "ERROR" transactions first. Then you will want to consider parsing back to numbers in the function you pass to comparing().

    public static void main(String[] args) {
    BankAccount<String> bankAccount = new BankAccount<>();
    bankAccount.addTransaction(Double.toString(2.00), TransactionType.DEPOSIT);
    bankAccount.addTransaction(Double.toString(100.66), TransactionType.DEPOSIT);
    bankAccount.addTransaction(Double.toString(2.00), TransactionType.WITHDRAW);
    bankAccount.addTransaction("ERROR", TransactionType.WITHDRAW);

    List<Transaction<String>> transactions = bankAccount.getTransactions();

    List<Transaction<String>> collect = transactions.stream()
            .filter(tr -> !tr.getAmount().equals("ERROR"))
            .sorted(Comparator.<Transaction<String>, String> comparing(transaction -> transaction.getAmount()).reversed())
            .collect(Collectors.toList());

    collect.forEach(tr -> System.out.println(tr.getAmount() + " " + tr.getType().name()));
}

public static class BankAccount<T> {

    private List<Transaction<T>> transactions = new ArrayList<>();

    public void addTransaction(T amount, TransactionType transactionType) {
        Transaction<T> transaction = new Transaction <>(amount, transactionType);
        transactions.add(transaction);
    }

    public List<Transaction<T>> getTransactions() {
        return Collections.unmodifiableList(transactions);
    }
}

public static class Transaction<T> {
    private final T amount;
    private final TransactionType transactionType;
    private final Date dateCreated;

    public Transaction(T amount, TransactionType transactionType) {
        this.amount = amount;
        this.transactionType = transactionType;
        this.dateCreated = new Date();
    }

    public T getAmount() {
        return amount;
    }

    public TransactionType getType(){
        return transactionType;
    }

    public Date getDateCreated(){
        return dateCreated;
    }

}

public enum TransactionType {
    DEPOSIT, WITHDRAW;
}
Comments