io.github.dinamo541.corefx.util

Answer

Value typeTipo valor final Serializable Cloneable

A standardized, immutable-friendly response object for operations. It bundles an outcome state (success/failure), a user-facing message, a technical internal message for logs, and an arbitrary keyed result payload — so every service method can return one consistent shape. Un objeto de respuesta estandarizado y amigable con la inmutabilidad para operaciones. Reúne un estado de resultado (éxito/fallo), un mensaje de cara al usuario, un mensaje interno técnico para los logs y una carga de resultados arbitraria e indexada — para que cada método de servicio pueda devolver una forma consistente.

Why a response wrapperPor qué un envoltorio de respuesta

Instead of throwing for expected failures or returning bare booleans, return an Answer: the caller checks isSuccess(), shows getMessage() to the user, logs getInternalMessage(), and reads typed results by key. The result map is a LinkedHashMap, so keys keep insertion order for deterministic toString(). En vez de lanzar excepciones ante fallos esperados o devolver booleanos pelados, devuelve un Answer: el llamante comprueba isSuccess(), muestra getMessage() al usuario, registra getInternalMessage() y lee resultados tipados por clave. El mapa de resultados es un LinkedHashMap, así las claves conservan el orden de inserción para un toString() determinista.

🛡️
Resilience guaranteesGarantías de resiliencia

The result map is final and never null. getResults() returns an unmodifiable view, so the only mutation path is setResult/with — external code can't corrupt the payload. Result keys may never be null; values may be.El mapa de resultados es final y nunca null. getResults() devuelve una vista no modificable, así que la única vía de mutación es setResult/with — el código externo no puede corromper la carga. Las claves de resultado nunca pueden ser null; los valores sí.

Quick startInicio rápido

// Build with static factories + the fluent builder:
return Answer.success("Account created")
             .with("userId", user.getId())
             .with("user", user);

// Failure:
return Answer.failure("E-mail already in use");

// Consume:
Answer a = service.register(form);
if (a.isSuccess()) {
    Long id = a.getResult("userId", Long.class);   // type-safe, null on mismatch
} else {
    Message.show(AlertType.ERROR, "Error", a.getMessage());
}

Static factoriesFábricas estáticas

FactoryFábricaProducesProduce
Answer.ok()Success, no messages.Éxito, sin mensajes.
Answer.success(message)Success with a user message.Éxito con un mensaje para el usuario.
Answer.success(message, internal)Success with user + internal messages.Éxito con mensajes para el usuario + internos.
Answer.failure(message)Failure with a user message.Fallo con un mensaje para el usuario.
Answer.failure(message, internal)Failure with user + internal messages.Fallo con mensajes para el usuario + internos.

Constructors are also available: no-arg, (state, message, internal), (state, message, internal, key, result), and a copy constructor Answer(Answer).También hay constructores disponibles: sin argumentos, (state, message, internal), (state, message, internal, key, result), y un constructor de copia Answer(Answer).

StateEstado

MethodMétodoMeaningSignificado
getState() / setState(Boolean)The raw flag — true, false, or null (never set).El indicador crudo — true, false o null (nunca asignado).
isSuccess()true only when state is non-null and true.true solo cuando el estado no es null y es true.
isFailure()true only when state is non-null and false.true solo cuando el estado no es null y es false.
💡
Tri-state on purposeTri-estado a propósito

Because state is a Boolean, an Answer can be "neither confirmed success nor failure" (null). Use isSuccess()/isFailure() rather than truth-testing getState() to handle that cleanly.Como el estado es un Boolean, un Answer puede ser "ni éxito ni fallo confirmados" (null). Usa isSuccess()/isFailure() en vez de evaluar la verdad de getState() para manejar eso con limpieza.

MessagesMensajes

getMessage() / setMessage() for the end-user text, and getInternalMessage() / setInternalMessage() for technical detail meant for logs and debugging. Both may be null.getMessage() / setMessage() para el texto del usuario final, y getInternalMessage() / setInternalMessage() para el detalle técnico destinado a logs y depuración. Ambos pueden ser null.

Result payloadCarga de resultados

void setResult(String key, Object value)  ·  Answer with(String key, Object value)

Add/replace an entry. with is the fluent variant returning this for chaining. Key must be non-null; value may be null.Añade/reemplaza una entrada. with es la variante fluida que devuelve this para encadenar. La clave no puede ser null; el valor sí.

Object getResult(String key)  ·  <T> T getResult(String key, Class<T> type)

Raw lookup, or a type-safe one that returns null when absent or of a different type — so it never throws ClassCastException.Búsqueda cruda, o una con tipo seguro que devuelve null cuando no existe o es de otro tipo — así nunca lanza ClassCastException.

Object getResultOrDefault(String key, Object def)  ·  boolean hasResult(String key)  ·  Object removeResult(String key)

Default-returning lookup, presence check, and removal (returns the previous value).Búsqueda con valor por defecto, comprobación de presencia y eliminación (devuelve el valor anterior).

Map<String,Object> getResults()

An unmodifiable view of all results — mutating it throws UnsupportedOperationException.Una vista no modificable de todos los resultados — mutarla lanza UnsupportedOperationException.

Copying & equalityCopia e igualdad

clone() and its cast-free alias copy() both produce an independent Answer via the copy constructor (a shallow copy of the result map — value references are shared). equals/hashCode/toString consider all fields, with deterministic ordering thanks to the LinkedHashMap. clone() y su alias sin cast copy() producen ambos un Answer independiente vía el constructor de copia (una copia superficial del mapa de resultados — las referencias de los valores se comparten). equals/hashCode/toString consideran todos los campos, con orden determinista gracias al LinkedHashMap.

⚠️
Shallow copy semanticsSemántica de copia superficial

copy() duplicates the map structure but shares the stored value objects. If a stored value is mutable and you mutate it, both answers see the change. Keep payload values immutable when that matters.copy() duplica la estructura del mapa pero comparte los objetos de valor almacenados. Si un valor almacenado es mutable y lo mutas, ambos answers ven el cambio. Mantén inmutables los valores de la carga cuando eso importe.

RecommendationsRecomendaciones

Do return Answer from service/business methods for a uniform success/failure contract across the app.Devuelve Answer desde los métodos de servicio/negocio para un contrato uniforme de éxito/fallo en toda la app.

Do use the typed getResult(key, type) to avoid casts and accidental ClassCastException.Usa el getResult(key, type) tipado para evitar casts y ClassCastException accidentales.

Do put the friendly text in message and the stack-trace-y detail in internalMessage — show one, log the other.Pon el texto amable en message y el detalle tipo stack-trace en internalMessage — muestra uno, registra el otro.