Определение рекурсивного универсального класса и ошибка компиляции «невозможно преобразовать» - PullRequest
0 голосов
/ 31 января 2012

Этот дизайн похож на другие шаблоны JPA BaseEntity, которые вы, возможно, видели:

@MappedSuperclass()
public abstract class Entity<X extends Entity<X>>
    implements
        Comparable<X>,
        Serializable
{
    private static final long serialVersionUID = 1L;
    private Long id;
    private Date timeStamp;

...

    //  Simply compare fields in subclass until difference is discovered
    private int compareSubclassFields(X that)
    {
        int result = 0;
        for(Comparator<X> comparator : getComparators())
        {
            result = comparator.compare(this,that); <<=== compilation error
            if(result != 0) { break; }
        }
        return result;
    }

    /**
     *  Entity subclasses provide a list of their own special
     *  comparators that can contribute to the comparison.
     */
    protected abstract List<Comparator<X>> getComparators();
}

Вот пример класса, расширяющего Entity:

public class User extends Entity<User>
{
    private static final long serialVersionUID = 1L;
    private String firstName;
    private String lastName;
    private String email;

...

    @Override
    public List<Comparator<User>> getComparators()
    {
        List<Comparator<User>> result =
            new ArrayList<Comparator<User>>();

        result.add(getLastNameComparator()); //  Sort first on last name
        result.add(getFirstNameComparator());//  Next, by first name
        result.add(getEmailComparator());    //  Finally, by email (unique)
        return result;
    }
}

Когда я компилируюЯ получаю следующую ошибку:

error: method compare in interface Comparator<T> cannot be 
       applied to given types;

        result = comparator.compare(this,that);
                                    ^
required: X,X
found: Entity<X>,X
reason: actual argument Entity<X> cannot be converted to
        X by method invocation conversion

where X,T are type-variables:
  X extends Entity<X> declared in class Entity
  T extends Object declared in interface Comparator

Чтение Определение Java Enum , в частности, часть, в которой написано:

public class StatusCode extends Enum<StatusCode>

Теперь, если выпроверьте ограничения, у нас есть Enum - так что E = StatusCode.Давайте проверим: расширяет ли E Enum?Да!Мы в порядке.

Я предполагаю, что в моем примере, где X extends Entity<X>, 'this' будет экземпляром User, а не Entity<User>.Более того, поскольку Entity является абстрактным классом, он должен быть расширен и, следовательно, compareNonIdFields может быть вызван только экземпляром X - на самом себе.Конечно, когда я произношу, я получаю неконтролируемое предупреждение:

warning: [unchecked] unchecked cast
        result = comparator.compare(((X)this),that);
                                        ^
required: X
found:    Entity<X>
where X is a type-variable:
  X extends Entity<X> declared in class Entity
1 warning

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

Ответы [ 3 ]

2 голосов
/ 31 января 2012

Вы пишете ключевое слово this внутри класса Entity<X>.Итак, this = Entity<X>

С другой стороны, вы предоставили Comparator для X, а не для Entity<X>.

Вы можете оставить поле длясохранить связанный X объект внутри Entity<X> объекта и написать таким образом:

result = comparator.compare(this.getX(),that);

1 голос
/ 31 января 2012

Представьте себе следующие два класса.

class Foo extends Entity<Bar> {}
class Bar extends Entity<Foo> {}

Очевидно, что сравнение может быть вызвано не только для экземпляров X: если вы вызываете его для экземпляра Foo, тогда X = Bar и т.versa.

Редактировать: Просто для ясности, хотя вы намерены всегда заменять сам наследующий тип на X, это не применяется языком и / или компилятором.Это источник вашей проблемы.

0 голосов
/ 03 февраля 2012

Weird! Это очень рад, если вы

result = comparator.compare((X)this, that);

Итак, есть ли обстоятельства, при которых "это" не может быть Х? Какая-то странная перестановка подклассов и оставление параметра несвязанным? Или дальнейшее создание подкласса подкласса со связанным параметром?

А га! Это может произойти, если вы создадите подкласс класса, когда X уже связан!

... нет, это не правильно. Должен признаться, я сбит с толку.

...