Ho scritto una semplice gerarchia di classi per rappresentare i termini in Scala. I termini sono tipi di dati ricorsivi, ad esempio un Sum
e un Multiplication
sono costituiti dal lato sinistro ( lhs
), che è un Termine e il lato destro ( rhs
), che è anche un termine.
La corrispondenza dei modelli di Scala semplifica la verifica della presenza di un termine in una determinata struttura. Ad esempio, ecco un metodo che restituisce t3
dal Term se è strutturato come t1 * (t2 + t3)
:
def fetchT3(t: Term): Option[Term] = Some(t).collect {
case Multiplication(t1, Sum(t2, t3)) => t3
}
Ora voglio scrivere lo stesso in Java, ma la mancanza di pattern matching lo rende difficile. Utilizzo dell'operatore instanceof
se disapprovato, ma non ho trovato una vera alternativa. Ecco cosa ho scoperto finora:
public static <T> Optional<T> maybeAs(final Object obj, final Class<T> clazz) {
try {
return Optional.of(clazz.cast(obj));
} catch (ClassCastException exc) {
return Optional.empty();
}
}
Il metodo fetchT3
dall'alto può quindi essere scritto come:
public Optional<Term> fetchT3(final Term t) {
return maybeAs(t, Multiplication.class).flatMap(mult ->
maybeAs(mult.rhs, Sum.class).map(sum ->
sum.rhs
)
);
}
Quello che non mi piace di questo approccio è che, per prima cosa, sta usando le eccezioni per situazioni che non sono veramente eccezionali, e in secondo luogo, diventerà difficile da leggere quando i termini sono profondamente annidati. L'utilizzo delle eccezioni può essere evitato scrivendo un metodo per ogni classe ( maybeAsSum
, maybeAsMultiplication
, ...) e quindi usando instanceof
, che invece introdurrebbe la ridondanza del codice. In entrambi i casi, le lezioni di casting sono generalmente viste come un sintomo di una progettazione scadente in Java, ma non conosco un modo per aggirarlo.
Quindi la mia domanda è, lo considereresti come un esempio di utilizzo accettabile del lancio di classe, o vedrai un metodo migliore per controllare la struttura dei termini?