Универсальное наследование Java "unchecked cast" - PullRequest
1 голос
/ 24 апреля 2019

Я внедрил простую систему обработки платежей с использованием java-дженериков.Он компилируется и работает во время выполнения, но меня смущает предупреждение «unchecked cast».

enum PaymentType {
    CARD, SAVED_CARD
}

interface PayData {
}

class CardPayData implements PayData {
    private String cardNumber;
    private String cvc;
}

class SavedCardPayData implements PayData {
    private String cardId;
}

interface PayService<T extends PayData> {
    void pay(T payData);
}

class CardPayService implements PayService<CardPayData> {
    @Override
    public void pay(CardPayData cardPayData) {
        // i need concrete class CardPayData here
    }
}

class SavedCardPayService implements PayService<SavedCardPayData> {
    @Override
    public void pay(SavedCardPayData payData) {
        // i need concrete class SavedCardPayData here
    }
}

class PayServiceFactory {
    private CardPayService cardPayService = new CardPayService();
    private SavedCardPayService savedCardPayService = new SavedCardPayService();

    public PayService getService(PaymentType paymentType) {
        if (paymentType.equals(PaymentType.CARD))
            return cardPayService;
        else
            return savedCardPayService;
    }
}

class PaymentProcessor {
    PayServiceFactory payServiceFactory = new PayServiceFactory();

    public void serveRequest(PayData payData, PaymentType paymentType) {
//        here i have 'unchecked cast' warning
        PayService<PayData> payService = (PayService<PayData>) payServiceFactory.getService(paymentType);
        payService.pay(payData);
    }
}

Любые попытки отойти от такого предупреждения приводят меня к ошибкам компиляции.Например, я попытался вернуть универсальный тип из фабричного метода и ничего не получил, кроме ошибки компиляции:

// return generic
public PayService<? extends PayData> getService(PaymentType paymentType) { ... }
 public void serveRequest(PayData payData, PaymentType paymentType) {
        PayService<? extends PayData> payService =  payServiceFactory.getService(paymentType);
// error here:
// pay (capture <? extends PayData>) in PayService cannot be applied to (PayData)
        payService.pay(payData);
    }

1 Ответ

0 голосов
/ 24 апреля 2019

Дизайн ваших классов по своей сути небезопасен. В частности, метод serveRequest:

public void serveRequest(PayData payData, PaymentType paymentType) {
    PayService<PayData> payService = (PayService<PayData>) payServiceFactory.getService(paymentType);
    payService.pay(payData);
}

Нет способа обеспечить, чтобы тип времени выполнения payData был совместим с тем, что ожидает payService.pay. В зависимости от paymentType, getService может возвращать PayService<Anything> во время выполнения, поэтому payService.pay может ожидать любой тип во время выполнения. Поскольку тип, которым должен быть payData, может быть известен только во время выполнения, вы не можете применить его во время компиляции.

Лучшее, что вы можете сделать, это поставить SuppressWarnings и сделать payData универсальным:

@SuppressWarnings("unchecked")
public <T extends PayData> void serveRequest(T payData, PaymentType paymentType) {
    PayService<T> payService = (PayService<T>) payServiceFactory.getService(paymentType);
    payService.pay(payData);
}
...