Гарантировать ковариантный тип возврата с помощью дженериков в Java
У меня есть класс под названием Point
, с методом neighbors()
, который возвращает массив Point
s:
public class Point {
public Point[] neighbors() { /* implementation not shown */ }
}
У меня есть подкласс Point
, называемый SpecialPoint
, который переопределяет neighbors()
, чтобы вернуть массив SpecialPoint
вместо Point
s. Я думаю, что это называется ковариантными типами возврата.
public class SpecialPoint extends Point {
public SpecialPoint[] neighbors() { /* implementation not shown */ }
}
В отдельном классе я хочу использовать Point
и SpecialPoint
с generics
public <P extends Point> P doStuff(P point) {
P[] neighbors = point.neighbors();
// more stuff here including return
}
Это не скомпилируется, потому что компилятор может гарантировать, что P
является некоторым подклассом Point
, но нет гарантии, что каждый подкласс Point
переопределит neighbors()
, чтобы вернуть массив из себя как Я, случается, сделал с SpecialPoint
, поэтому Java знает, что P#neighbors()
возвращает Point[]
, а не P[]
.
Как я гарантирую, что каждый подкласс переопределяет neighbors()
с помощью ковариантного типа возврата, чтобы я мог использовать его с generics?
Ответы
Ответ 1
Вы можете использовать интерфейс:
public interface Point<P extends Point<P>> {
P[] neighbors();
}
public class SimplePoint implements Point<SimplePoint> {
@Override
public SimplePoint[] neighbors() { /* ... */ }
}
public class SpecialPoint implements Point<SpecialPoint> {
@Override
public SpecialPoint[] neighbors() { /* ... */ }
}
Тогда:
public <P extends Point<P>> P doStuff(P point) {
P[] neighbors = point.neighbors();
/* ... */
}
Если вам все еще нужно разложить код между реализациями, тогда лучше использовать абстрактный класс:
public abstract class Point<P extends Point<P>> {
public abstract P[] neighbors();
public void commonMethod() { /* ... */ }
}
public class SimplePoint extends Point<SimplePoint> { /* ... */ }
public class SpecialPoint extends Point<SpecialPoint> { /* ... */ }
Ответ 2
Возможно, interface Point
решает вашу проблему:
public class Test
{
public interface Point {
public Point[] neighbors();
}
public class SpecialPoint implements Point {
public SpecialPoint[] neighbors() { return null; }
}
public class SpecialPoint2 implements Point {
public SpecialPoint2[] neighbors() { return null; }
}
public Point doStuff(SpecialPoint point) {
Point[] neighbors = point.neighbors();
return neighbors[0];
}
public Point doStuff(SpecialPoint2 point) {
Point[] neighbors = point.neighbors();
return neighbors[0];
}
}