suma - Llamada a un método impar en Java usando un operador de puntos para acceder a una lista genérica
numeros pares e impares en java con arreglos (5)
Básicamente, el método tryExecute()
en ConnectionHelper
usa genéricos. Esto le permite ingresar la inferencia de tipo antes de la llamada al método después del "operador de punto". Esto se muestra directamente en los tutoriales de Oracle Java para Generics, aunque lo consideraría una mala práctica en un entorno de producción.
Puedes ver un ejemplo oficial aquí .
Como puede ver en su publicación modificada, la definición de tryExecute()
es:
public static <T> T tryExecute(Callable<T> command)
Al <List<ClientCoverageCRU>> tryExcute
como tal ( <List<ClientCoverageCRU>> tryExcute
), está forzando que T
sea una List<ClientCoverageCRU>
. Una mejor práctica en general, sin embargo, sería dejar que esto se infiera de un argumento real en el método. El tipo también se puede inferir a partir del Callable<T>
, por lo que se le proporciona un Callable<List<ClientCoverageCRU>>
como argumento que eliminaría la necesidad de este uso confuso.
Consulte su uso en JLS 4.11 - Dónde se usan los tipos :
<S> void loop(S s) { this.<S>loop(s); }
... y la definición formal de por qué está permitido en la invocación de métodos en JLS 15.12 - Expresiones de invocación de métodos . Puede saltar a 15.12.2.7 y 15.12.2.8 para obtener más detalles. 15.12.2.8 - Inferir Argumentos de Tipo no Resueltos explica la lógica formal por la cual esto funciona.
Encontré un código avanzado de Java (avanzado para mí :)) Necesito ayuda para entender.
En una clase hay una clase anidada como a continuación:
private final class CoverageCRUDaoCallable implements
Callable<List<ClientCoverageCRU>>
{
private final long oid;
private final long sourceContextId;
private CoverageCRUDaoCallable(long oid, long sourceContextId)
{
this.oid = oid;
this.sourceContextId = sourceContextId;
}
@Override
public List<ClientCoverageCRU> call() throws Exception
{
return coverageCRUDao.getCoverageCRUData(oid, sourceContextId);
}
}
Más adelante en la clase externa, hay una instancia de la clase invocable que se está creando. No tengo idea de qué es esto:
ConnectionHelper.<List<ClientCoverageCRU>> tryExecute(coverageCRUDaoCallable);
No me parece una sintaxis java. ¿Podría por favor elaborar lo que está pasando en esta sintaxis críptica? Puede ver que se usa a continuación en el extracto del código.
CoverageCRUDaoCallable coverageCRUDaoCallable = new CoverageCRUDaoCallable(
dalClient.getOid(), sourceContextId);
// use Connection helper to make coverageCRUDao call.
List<ClientCoverageCRU> coverageCRUList = ConnectionHelper
.<List<ClientCoverageCRU>> tryExecute(coverageCRUDaoCallable);
EDITED agregó la clase ConnectionHelper.
public class ConnectionHelper<T>
{
private static final Logger logger =
LoggerFactory.getLogger(ConnectionHelper.class);
private static final int CONNECTION_RETRIES = 3;
private static final int MIN_TIMEOUT = 100;
public static <T> T tryExecute(Callable<T> command)
{
T returnValue = null;
long delay = 0;
for (int retry = 0; retry < CONNECTION_RETRIES; retry++)
{
try
{
// Sleep before retry
Thread.sleep(delay);
if (retry != 0)
{
logger.info("Connection retry #"+ retry);
}
// make the actual connection call
returnValue = command.call();
break;
}
catch (Exception e)
{
Throwable cause = e.getCause();
if (retry == CONNECTION_RETRIES - 1)
{
logger.info("Connection retries have exhausted. Not trying "
+ "to connect any more.");
throw new RuntimeException(cause);
}
// Delay increased exponentially with every retry.
delay = (long) (MIN_TIMEOUT * Math.pow(2, retry));
String origCause = ExceptionUtils.getRootCauseMessage(e);
logger.info("Connection retry #" + (retry + 1)
+ " scheduled in " + delay + " msec due to "
+ origCause);
+ origCause);
}
}
return returnValue;
}
Con frecuencia piensa que las clases son genéricas, pero los métodos también pueden ser genéricos. Un ejemplo común es Arrays.asList
.
La mayoría de las veces, no tiene que usar la sintaxis con corchetes angulares <...>
, incluso cuando está invocando un método genérico, porque este es el único lugar en el que el compilador de Java es realmente capaz de hacer básicos escriba inferencia en algunas circunstancias. Por ejemplo, el fragmento de la documentación de Arrays.asList
omite el tipo:
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
Pero es equivalente a esta versión en la cual el tipo genérico se da explícitamente:
List<String> stooges = Arrays.<String>asList("Larry", "Moe", "Curly");
Desde Java Generics and Collections
,
List<Integer> ints = Lists.<Integer>toList(); // first example
List<Object> objs = Lists.<Object>toList(1, "two"); // second example
-
In the first example
, sin el parámetro tipo, hay muy poca información para el algoritmo de inferencia de tipo utilizado por el compilador de Sun para inferir el tipo correcto. Se infiere que el argumento de toList es una matriz vacía de un tipo genérico arbitrario en lugar de una matriz vacía de enteros, y esto desencadena la advertencia no verificada descrita anteriormente. (El compilador de Eclipse usa un algoritmo de inferencia diferente y compila la misma línea correctamente sin el parámetro explícito). -
In the second example
, sin el parámetro tipo hay demasiada información para el algoritmo de inferencia de tipo para inferir el tipo correcto. Puede pensar que Object es el único tipo que un entero y una cadena tienen en común, pero de hecho ambos también implementan las interfaces Serializable y Comparable. El algoritmo de inferencia de tipo no puede elegir cuál de estos tres es el tipo correcto.
En general, la siguiente regla general es suficiente:
En una llamada a un método genérico, si hay uno o más argumentos que corresponden a un parámetro de tipo y todos tienen el mismo tipo, entonces el parámetro de tipo puede inferirse; si no hay argumentos que se correspondan con el parámetro de tipo o si los argumentos pertenecen a diferentes subtipos del tipo previsto, entonces el parámetro de tipo se debe dar explícitamente.
Algunos puntos para pasar el parámetro de tipo
Cuando se pasa un parámetro de tipo a una invocación de método genérico, aparece entre corchetes angulares a la izquierda, al igual que en la declaración del método.
La gramática de Java requiere que los parámetros de tipo solo aparezcan en las invocaciones de métodos que usan una forma punteada. Incluso si el método toList se define en la misma clase que invoca el código, no podemos acortarlo de la siguiente manera:
List<Integer> ints = <Integer>toList(); // compile-time error
Esto es ilegal porque confundirá al analizador.
Es feo, pero válido.
Independientemente de ConnectionHelper
es, tiene un método estático tryExecute
que necesita inferir un tipo genérico.
Algo como:
public static <T> T tryExecute() { ... }
Editar desde la pregunta actualizada: Java tiene inferencia de tipo para tipos genéricos. La primera <T>
en la firma del método significa que el tipo se deducirá cuando se llame al método.
En su publicación actualizada, muestra tryExecute()
definido para tomar un argumento genérico:
public static <T> T tryExecute(Callable<T> command)
Esto realmente significa que el uso de esa sintaxis es completamente redundante e innecesario; T
(el tipo) se deduce del command
está pasando en el que tiene que implementar Callable<T>
. El método se define para devolver algo del tipo inferido T
Infer a type
|
v
public static <T> T tryExecute(Callable<T> command)
^ ^
| |
<-return type--------------------------
En su ejemplo, coverageCRUDaoCallable
tiene que implementar Callable<List<ClientCoverageCRU>>
porque el método devuelve List<ClientCoverageCRU>
En mi ejemplo anterior, debería usar la sintaxis sobre la que preguntaba porque no se está transfiriendo nada para deducir el tipo. T
tiene que ser provisto explícitamente usando ConnectionHelper.<List<ClientCoverageCRU>>tryExecute()
Esto se debe a que, hasta Java 7, los genéricos no son totalmente compatibles con la tipificación de destino, por lo que debe ayudar al compilador un poco con lo que se llama un testigo de tipo como en ConnectionHelper.<List<ClientCoverageCRU>>
.
Sin embargo, tenga en cuenta que Java 8 mejora significativamente la tipificación de objetivos y en su ejemplo específico, el testigo de tipo no es necesario en Java 8.