Я использую ниже код на сегодняшний день и дату базы данных.
(TIMESTAMPDIFF(MINUTE,NOW(),EndDate))%60 as Minutes
Здесь мы можем использовать HOUR (в течение часа -% 24), DAY (в течение дней - не нужно%) вместо MINUTE, если нам нужно.
Используя некоторые скрытые функции javac
, мы можем получить более подробную информацию о том, что происходит:
$ javac -XDverboseResolution=deferred-inference,success,applicable LambdaInference.java
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo(value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Object>)Object)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo(value -> true).booleanValue(); // Compile error here
^
instantiated signature: (Bar<Object>)Object
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: error: cannot find symbol
Foo.foo(value -> true).booleanValue(); // Compile error here
^
symbol: method booleanValue()
location: class Object
1 error
Это много информации, давайте сломаем его .
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo(value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Object>)Object)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
фаза: применяется применимость фаза : фактические аргументы, переданные в аргументах типа-args: явные типы: потенциально применимые методы
имеет значение <none>
, поскольку наша неявно типизированная лямбда не является , относящейся к применимости .
Компилятор разрешает ваш вызов foo
единственному методу назван foo
в Foo
. Он частично был создан для Foo.<Object> foo
(поскольку не было никаких фактических данных или типов-аргументов), но это может измениться на стадии отложенного вывода.
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo(value -> true).booleanValue(); // Compile error here
^
instantiated signature: (Bar<Object>)Object
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
экземплярная подпись: полностью созданная подпись foo
. Это результат этого шага (в этот момент больше не будет сделан вывод о типе на подпись foo
). target-type: контекст, в который делается вызов. Если вызов метода является частью назначения, он будет левым. Если вызов метода сам по себе является частью вызова метода, это будет тип параметра.
Поскольку ваш вызов метода свисает, нет целевого типа. Поскольку нет целевого типа, больше никаких выводов на foo
и T
не предполагается Object
.
Компилятор не использует неявно типизированные лямбда во время вывода. В определенной степени это имеет смысл. В общем, учитывая param -> BODY
, вы не сможете скомпилировать BODY
, пока у вас не будет типа для param
. Если вы попытались вывести тип для param
из BODY
, это может привести к проблеме типа курица и яйцо. Возможно, некоторые улучшения будут сделаны в этом в будущих выпусках Java.
Foo.<Boolean> foo(value -> true)
Это решение обеспечивает явный тип аргумент foo
(обратите внимание на раздел with type-args
ниже). Это изменяет частичное копирование сигнатуры метода на (Bar<Boolean>)Boolean
, что вы хотите.
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.<Boolean> foo(value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: <none>
with type-args: Boolean
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Boolean>)Boolean)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: resolving method booleanValue in type Boolean to candidate 0
Foo.<Boolean> foo(value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: booleanValue()
Foo.foo((Value<Boolean> value) -> true)
Это решение явно вводит вашу лямбду, что позволяет учесть его применимость (примечание with actuals
ниже). Это изменяет частичную инстанцировку сигнатуры метода на (Bar<Boolean>)Boolean
, что вам нужно.
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo((Value<Boolean> value) -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: Bar<Boolean>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Boolean>)Boolean)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo((Value<Boolean> value) -> true).booleanValue(); // Compile error here
^
instantiated signature: (Bar<Boolean>)Boolean
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: resolving method booleanValue in type Boolean to candidate 0
Foo.foo((Value<Boolean> value) -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: booleanValue()
Foo.foo((Bar<Boolean>) value -> true)
То же, что и выше, но с немного отличающийся вкус.
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo((Bar<Boolean>) value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: Bar<Boolean>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Boolean>)Boolean)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo((Bar<Boolean>) value -> true).booleanValue(); // Compile error here
^
instantiated signature: (Bar<Boolean>)Boolean
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: resolving method booleanValue in type Boolean to candidate 0
Foo.foo((Bar<Boolean>) value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: booleanValue()
Boolean b = Foo.foo(value -> true)
Это решение обеспечивает явную цель для вызова вашего метода (см. target-type
ниже). Это позволяет отложенному-инстанцированию выводить, что параметр типа должен быть Boolean
вместо Object
(см. instantiated signature
ниже).
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Boolean b = Foo.foo(value -> true);
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Object>)Object)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Boolean b = Foo.foo(value -> true);
^
instantiated signature: (Bar<Boolean>)Boolean
target-type: Boolean
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
Это поведение, которое происходит. Я не знаю, является ли это то, что указано в JLS. Я мог бы копаться и посмотреть, могу ли я найти точный раздел, который определяет это поведение, но нотация типа ввода дает мне головную боль.
Это также не полностью объясняет, почему изменение Bar
для использования raw Value
исправит эту проблему:
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo(value -> true).booleanValue();
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Object>)Object)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo(value -> true).booleanValue();
^
instantiated signature: (Bar<Boolean>)Boolean
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: resolving method booleanValue in type Boolean to candidate 0
Foo.foo(value -> true).booleanValue();
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: booleanValue()
По какой-то причине изменение этого параметра на использование raw Value
позволяет отложенному экземпляру сделать вывод о том, что T
является Boolean
. Если бы мне пришлось размышлять, я бы предположил, что, когда компилятор пытается привязать лямбда к Bar<T>
, он может сделать вывод, что T
- Boolean
, глядя на тело лямбда. Это означает, что мой предыдущий анализ неверен. Компилятор может выполнять вывод типа в теле лямбда, но только в переменных типа, которые только появляются в возвращаемом типе.
Легкий способ исправить это объявление типа в вызове метода foo
:
Foo.<Boolean>foo(value -> true).booleanValue();
Редактировать: Я не могу найти конкретную документацию о почему это необходимо, почти так же, как и все остальные. Я подозревал, что это может быть из-за примитивных типов, но это было неправильно. Независимо от того, этот синтаксис вызывается с использованием целевого типа . Также Тип цели в Lambdas . Однако причины не ускользают от меня, я не могу найти документацию в любом месте о том, почему этот конкретный случай использования необходим.
Редактировать 2: Я нашел этот вопрос:
Вывод общего типа не работает с цепочкой методов?
Похоже, это потому, что вы цепляете методы здесь. Согласно комментариям JSR, указанным в принятом ответе, это было преднамеренное упущение функциональности, потому что у компилятора нет способа передать полученную информацию общего типа между цепными вызовами метода в обоих направлениях. В результате весь тип стираемого по времени до вызова booleanValue
. Добавление целевого типа удаляет это поведение, предоставляя ограничение вручную, а не позволяя компилятору принять решение, используя правила, изложенные в JLS §18 , что, похоже, не упоминает об этом вообще. Это единственная информация, которую я мог бы придумать. Если кто-нибудь найдет что-нибудь лучше, я бы хотел его увидеть.
T apply(Value value);
будет иметь какой-либо положительный эффект, но это произойдет.
– sstan
5 July 2015 в 06:50
Boolean b = Foo.foo(value -> true);
Синтаксис, который вы описываете (Foo.<Boolean> foo(value->true)
), известен как type witness i>.
– Jeffrey
5 July 2015 в 18:59
Я не знаю, почему, но вам нужно добавить отдельный тип возвращаемого значения:
public class HelloWorld{
static class Value<T> {
}
@FunctionalInterface
interface Bar<T,R> {
R apply(Value<T> value); // Return type added
}
static class Foo {
public static <T,R> R foo(Bar<T,R> callback) {
return callback.apply(new Value<T>());
}
}
void test() {
System.out.println( Foo.foo(value -> true).booleanValue() ); // No compile error here
}
public static void main(String []args){
new HelloWorld().test();
}
}
какой-нибудь умный парень, вероятно, может это объяснить.
value
в выражении лямбда выведено на Value<Object>
вместо Value<Boolean>
, что все равно вызовет проблемы в моем реальном сценарии, где тело выражения лямбда содержит больше кода ...
– Josh Stone
5 July 2015 в 05:28
static class Value<T extends Boolean>, interface Bar<T extends Boolean,R>, public static <T extends Boolean,R> R foo(Bar<T,R> callback)
и т. Д.
– fukanchik
5 July 2015 в 05:48
Значение будет вызывать тип Value<Object>
, потому что вы неправильно интерпретировали лямбда. Подумайте об этом, как вы называете лямбдой непосредственно методом применения. Итак, что вы делаете:
Boolean apply(Value value);
, и это правильно выводится на:
Boolean apply(Value<Object> value);
, поскольку вы не указали тип значения.
Правильно вызовите лямбда:
Foo.foo((Value<Boolean> value) -> true).booleanValue();
это будет выведено на:
Boolean apply(Value<Boolean> value);
Ваше решение должно быть немного более ясным. Если вам нужен обратный вызов, вам потребуется значение типа, которое будет возвращено.
Я создал общий интерфейс обратного вызова, общий класс Value и UseClass, чтобы показать, как его использовать.
/**
*
* @param <P> The parameter to call
* @param <R> The return value you get
*/
@FunctionalInterface
public interface Callback<P, R> {
public R call(P param);
}
public class Value<T> {
private final T field;
public Value(T field) {
this.field = field;
}
public T getField() {
return field;
}
}
public class UsingClass<T> {
public T foo(Callback<Value<T>, T> callback, Value<T> value) {
return callback.call(value);
}
}
public class TestApp {
public static void main(String[] args) {
Value<Boolean> boolVal = new Value<>(false);
Value<String> stringVal = new Value<>("false");
Callback<Value<Boolean>, Boolean> boolCb = (v) -> v.getField();
Callback<Value<String>, String> stringCb = (v) -> v.getField();
UsingClass<Boolean> usingClass = new UsingClass<>();
boolean val = usingClass.foo(boolCb, boolVal);
System.out.println("Boolean value: " + val);
UsingClass<String> usingClass1 = new UsingClass<>();
String val1 = usingClass1.foo(stringCb, stringVal);
System.out.println("String value: " + val1);
// this will give you a clear and understandable compiler error
//boolean val = usingClass.foo(boolCb, stringVal);
}
}
value -> true
, для которой значение будет выведено на Value<Boolean>
, и метод, принимающий лямбда, вернется Boolean
. Любые другие изменения прекрасны, но они должны оставаться на месте, чтобы использовать API.
– Josh Stone
5 July 2015 в 17:40
foo
принимал один аргумент, чтобы сделать эту работу.
– Josh Stone
5 July 2015 в 17:49
Как и другие ответы, я также надеюсь, что кто-то умнее может указать , почему компилятор не может сделать вывод, что T
- Boolean
.
Один путь чтобы помочь компилятору поступать правильно, без каких-либо изменений в вашем существующем дизайне класса / интерфейса, явным образом объявляю тип формального параметра в вашем лямбда-выражении. Итак, в этом случае, явно объявив, что тип параметра value
равен Value<Boolean>
.
void test() {
Foo.foo((Value<Boolean> value) -> true).booleanValue();
}
Foo.foo
выводит тип возвращаемого значения boolean, а value
в лямбда выведен на Value<Boolean>
. Я не хочу показывать, так как это часть публичного API и требует, чтобы приведение создало проблему удобства использования.
– Josh Stone
5 July 2015 в 05:49
Вывод по типу lambda не может зависеть от тела лямбда.
Компилятор сталкивается с трудной задачей, пытаясь понять неявные лямбда-выражения
foo( value -> GIBBERISH )
Тип value
должен быть выведен первым до того, как GIBBERISH может быть скомпилирован, поскольку в целом интерпретация GIBBERISH зависит от определения value
.
(В вашем специальном случае GIBBERISH оказывается простой константой, независимой от value
.)
Javac должен сначала вывести Value<T>
для параметра value
; в контексте нет ограничений, поэтому T=Object
. Затем тело лямбда true
скомпилировано и распознается как логическое, совместимое с T
.
После того, как вы внесли изменение в функциональный интерфейс, тип параметра лямбда не требует вывода; T остается неосновным. Затем тело лямбда скомпилировано, и тип возврата выглядит как Boolean, который устанавливается как нижняя граница для T
.
Еще один пример, демонстрирующий проблему
<T> void foo(T v, Function<T,T> f) { ... }
foo("", v->42); // Error. why can't javac infer T=Object ?
T, считается String
; тело лямбды не принимало участия в выводе.
В этом примере поведение Javac кажется нам очень разумным; это, вероятно, предотвратило ошибку программирования. Вы не хотите, чтобы вывод был слишком сильным; если все, что мы пишем, компилируется каким-то образом, мы потеряем доверие к ошибкам компилятора для нас.
Существуют и другие примеры, когда тело лямбда, как представляется, обеспечивает однозначные ограничения, однако компилятор не может использовать эту информацию , В Java сначала должны быть определены типы параметров лямбда, прежде чем можно будет рассмотреть тело. Это преднамеренное решение. Напротив, C # хочет попробовать разные типы параметров и посмотреть, что делает компиляцию кода. Java считает это слишком рискованным.
В любом случае, когда неявная лямбда не выполняется, что происходит довольно часто, укажите явные типы для лямбда-параметров; в вашем случае (Value<Boolean> value)->true