Метод Java неограниченный тип или возврат класса
Я читал этой статья,, о подклассах класса builder. Я понял статью, но был один маленький кусочек, который беспокоил меня. Был такой метод,
public static Builder<?> builder() {
return new Builder2();
}
когда я изменил Builder<?>
to Builder
, необработанный тип, компилятор не будет компилировать код. Ошибка была,
Rectangle.java:33: error: cannot find symbol
System.out.println(Rectangle.builder().opacity(0.5).height(250);
какая дополнительная информация была передана компилятору с помощью дополнительного <?>
? Я подозревал, что это был компилятор, который не мог понять правый экземпляр во время компиляции. Если я удалю маркеры комментариев в (A), код скомпилирован и работает нормально. Все это время он ссылался на экземпляр Rectangle. Итак, я предполагаю, что это был компилятор, который потерпел неудачу.
было бы здорово, если кто-то может указать мне на статью, которая объясняет это или приводит к более подробной информации об этом. Спасибо.
я вставил код здесь:
public class Shape {
private final double opacity;
public static class Builder<T extends Builder<T>> {
private double opacity;
public T opacity(double opacity) {
this.opacity = opacity;
return self();
}
/* Remove comment markers to make compilation works (A)
public T height(double height) {
System.out.println("height not set");
return self();
}
*/
protected T self() {
System.out.println("shape.self -> " + this);
return (T) this;
}
public Shape build() {
return new Shape(this);
}
}
public static Builder<?> builder() {
return new Builder();
}
protected Shape(Builder builder) {
this.opacity = builder.opacity;
}
}
public class Rectangle extends Shape {
private final double height;
public static class Builder<T extends Builder<T>> extends Shape.Builder<T> {
private double height;
public T height(double height) {
System.out.println("height is set");
this.height = height;
return self();
}
public Rectangle build() {
return new Rectangle(this);
}
}
public static Builder<?> builder() {
return new Builder();
}
protected Rectangle(Builder builder) {
super(builder);
this.height = builder.height;
}
public static void main(String[] args) {
Rectangle r = Rectangle.builder().opacity(0.5).height(250).build();
}
}
3 ответов
какая дополнительная информация была передана компилятору с помощью дополнительные
<?>
?
дополнительная информация с помощью подстановочного знака <?>
было, что то вернулось Rectangle.Builder<?>
Это супер класс всех возможных generic Rectangle.Builder<T>
классы (см. Шаблоны). И с Rectangle.Builder<T>
гарантированно имеет аргумент типа T, который сам является подклассом Rectangle.Builder
, пока его общий тип не является игнорируется, Rectangle.Builder<?>
также гарантированно будет по крайней мере типа Rectangle.Builder<? extends Rectangle.Builder<?>>
. Если вы полностью проигнорируете дженерики, удалив подстановочный знак, эта информация будет потеряна и код будет скомпилирован как обычный pre-Java5.0 код (где дженериков не существовало). Это необходимо для обратной совместимости.
чтобы увидеть разницу, рассмотрим подкласс прямоугольник.Конструктор, который игнорирует общий тип:
public static class BadBuilder extends Rectangle.Builder {
private double height;
public BadBuilder height(double height) {
System.out.println("height is set");
this.height = height;
return (BadBuilder) self();
}
@Override
public Shape.Builder opacity(double opacity) {
return new Shape.Builder();
}
public Rectangle build() {
return new Rectangle(this);
}
}
обратите внимание, что этот класс перезаписывает Shape.Builder#opacity
без возврат подкласса самого по себе. Компилятор не будет генерировать ошибки для этого класса (но он может предупредить вас, что класс игнорирует общего ввода). Таким образом, без общей информации это правовой, чтобы вернуть типа Shape.Builder
из метода непрозрачность. Как только вы добавите аргумент типа в BadBuilder, этот код больше не будет компилироваться:
public static class BadBuilder extends Rectangle.Builder<BadBuilder> // -> compile time error
Итак, причина, по которой вы получаете ошибку компилятора cannot find symbol
есть, потому что класс!--12--> не объявляет сам метод / символ T Shape.Builder#heigth()
, и указанный метод T Shape.Builder#opacity()
гарантирует только, что возвращаемый объект имеет тип Shape.Builder
, как объявлено в аргументе типа class Shape.Builder<T extends Shape.Builder<T>>
. Поэтому вызов цепочки методов Rectangle.builder().opacity(0.5).height(250)
будет работать только, если Rectangle.builder()
фактически гарантированно возвращает построитель,который набирается с подклассом Rectangle.Строитель. И эта гарантия может быть предоставлена только в том случае, если общий тип не игнорируется (как видно в BadBuilder образец.)
при добавлении метода Shape.Builder#heigth
, удалив комментарий в вашем коде, эта ошибка, очевидно, уходит, потому что тогда
эта разница заключается в том, что когда вы используете необработанный тип в методе, он превращает дженерики для всех вещей, которые вы делаете с этим типом.
например,Builder
У метод foo()
что возвращает List<String>
. Если вы позвоните foo()
выражение типа Builder<?>
, это будет типа List<String>
. С другой стороны, если вызов foo()
на выражении необработанного типа Builder
, тип этого выражения List
, а не List<String>
, хотя типа List<String>
не связано с T
на всех. Он обрабатывается, как если бы метод - это стирание того, что на самом деле есть.
Итак, в вашем случае предположим Rectangle.builder()
возвращаемого типа Rectangle.Builder<?>
. Для удобства давайте дадим этому название ?
, сказал X
. Так у вас есть Rectangle.Builder<X>
(который наследует от Shape.Builder<X>
) и вы называете opacity()
, который в X
. Мы знаем, потому что X
параметр типа Rectangle.Builder
, X
должен быть подтипом Rectangle.Builder<X>
. Так что мы можем позвонить height()
на нем.
Rectangle.builder()
возвращается тип raw Rectangle.Builder
, а вы называете opacity()
на нем он отключает дженерики по методу opacity()
, поэтому он возвращает стирание своего возвращаемого типа, который является Shape.Builder
. И вы не можете позвонить height()
об этом.я тот, кто задает подобные вопросы. Благодаря ответам Бальдр и newacct. Я попытался суммировать его в слове непрофессионала, которое я могу запомнить.
- без
<?>
, компилятор знает только возвращаемый типRectangle.Builder
иRectangle.Builder
является наследникомShape.Builder
без какой-либо другой информации. Так как по определениюT Shape.Builder#Opacity()
в классеShape.Builder<T extends Shape.Builder<T>
, лучшее знание для компилятора заключается в том, что возвращаемыйT
является наследникомShape.Builder
, следовательно, методopacity()
возвращает значение типаShape.Builder
и этот тип не может получить доступ к методуheight()
. -
С
<?>
, компилятор знает- возвращаемый тип
Rectangle.Builder<Something>
; - этого типа что-то по определению является подклассом
Rectangle.Builder
, потому чтоT extends Rectangle.Builder<T>
; - возвращаемый тип также является подклассом
Shape.Builder<Something>
, потому что в определенииRectangle.Builder<T ...> extends Shape.Builder<T>
.
- возвращаемый тип
By пункт 3, компилятор знает, что T Shape.Builder#opacity()
возвращает T
типа что-то; By пункт 2, компилятор знает, что тип что-то является наследником Rectangle.Builder
, Так что после вызова метода opacity()
, возвращаемый тип может получить доступ к Rectangle.Builder
's метод height()
.
я надеюсь, что компилятор действительно думает, как указано выше.