Динамическое приведение в Java - PullRequest
3 голосов
/ 15 октября 2010

До того, как я упрекнул меня за то, что я не выполнил свою домашнюю работу, я не смог найти никаких подсказок по множеству вопросов о дженериках Java и динамическом приведении.

Тип Скаляр определяется следующим образом:

public class Scalar <T extends Number> {

  public final String name;

  T value;

  ... 

  public T getValue() {
    return value;
  }

  public void setValue(T val) {
    this.value = val;      
  }

}

Я хотел бы иметь метод, который выглядит следующим образом:

public void evilSetter(V val) {
  this.value = (T) val;
}

Конечно, это обычно не рекомендуется. Причина, по которой я хочу такой метод, заключается в том, что у меня есть коллекция скаляров, значения которых я бы хотел изменить позже. Однако, как только они попадают в коллекцию, их параметры общего типа больше не доступны. Поэтому, даже если я хочу сделать назначение, которое будет полностью допустимым во время выполнения, нет никакого способа узнать, что оно будет действительным во время компиляции, с генериками или без них.

Map<String, Scalar<? extends Number>> scalars = ...;

Scalar<? extends Number> scalar = scalars.get("someId");

// None of this can work
scalar.value = ...
scalar.setValue(...)

Так как мне реализовать проверенный метод приведения и установки?

public <V extends Number> void castAndSet(V val) {
    // One possibility
    if (this.value.getClass().isAssignableFrom(val.getClass()) {
       // Some cast code here
    }
    // Another
    if (this.value.getClass().isInstanceOf(val) {
       // Some cast code here    
    }
    // What should the cast line be?
    // It can't be:
    this.value = this.value.getClass().cast(val);
    // Because this.value.getClass() is of type Class<?>, not Class<T>        

}

Так что мне осталось использовать

this.value = (T) val;

и перехват исключения ClassCastException?

1 Ответ

3 голосов
/ 15 октября 2010

У вас есть:

this.value.getClass().isAssignableFrom(val.getClass())

Это, вероятно, будет проблемой, если вы не уверены, что value никогда не будет нулевым.

У вас также есть:

this.value = (T) val;

Это приведёт только к Number, а не к T, потому что под капотом T это просто Number из-за стирания типа. Поэтому, если value является Double и val является Integer, исключение не будет выдано.

Если вы действительно хотите выполнить проверено приведение, у вас должен быть правильный Class<T> объект. Это означает, что вы должны передавать Class<T> в конструктор вашего объекта. (Если вы не уверены, что value никогда не бывает нулевым, в этом случае вы можете использовать свою первую идею.) Получив этот объект (сохраненный в поле), вы можете выполнить проверенное приведение:

T value = valueClass.cast(val);
...