Java catch-Block ist nicht nachvollziehbar unreachable geworden

mental.dIseASe

Lieutenant
Registriert
Dez. 2008
Beiträge
665
Guten Abend,

ich sitze seit vier Stunden an einem merkwürdigen Problem, das mir übelst den Kopf zermartert. Irgendwann im Verlauf des letzten ~3/4 Jahr ist in unserer Software ein catch-Block unreachable geworden. Ein Kollege hat den Code dann offenbar entfernt, weil der Compiler sonst wohl gemeckert hätte. Das Problem ist, dass der Code eigentlich sinnvoll war. Ich kann aber beim besten Willen in der git-History nicht erkennen, welche Änderung im Code selbst oder in Dependencies dieses compilerseitig gebotene Entfernen verursacht haben könnte.

Etwas Code:

Manager:

Code:
public interface EntityManager {
	public void save(Entity anEntity);
}

Außerdem haben wir eine von Exception abgeleitete Klasse:

Code:
public class AspectException extends Exception {}

Diese Exceptions werden von einem Aspekt generiert, der sämtliche Manager-Methoden umgarnt, um Exceptions aus der Datenhaltungsschicht hübsch zu verpacken.

Die Komponente, die den Manager verwurstet, sieht neuerdings so aus:

Code:
@Component
public class ConsumingClass {

	@Autowired private EntityManager entityManager;

	private void useTheManager(final Entity someEntity) {
		try {
			entityManager.save(someEntity);
		} catch (final Exception exception) {
			// some fallback exception handling
		}
	}
}

Vor diesem 3/4 Jahr war aber folgendes möglich:

Code:
@Component
public class ConsumingClass {

	@Autowired private EntityManager entityManager;

	private void useTheManager(final Entity someEntity) {
		try {
			entityManager.save(someEntity);
		} catch (final AspectException aspectException) {
			// some elaborate exception handling
		} catch (final Exception exception) {
			// some fallback exception handling
		}
	}
}

Das wirft der Compiler mir jetzt aber vor die Füße. Angesichts von http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21 frage ich mich zwar, wie das vorher funktionieren konnte. Wenn ich aber den alten Stand mit diesem Code auschecke, baut es ohne Probleme.

An der Implementierung des EntityManagers hat sich in meinen Augen nichts geändert, das ich als dafür verantwortlich einstufen würde. Weder die Methode im Interface noch in der Implementierung hat irgendwie ihre Signatur geändert. Die Implementierung wird aber inzwischen nicht mehr per XML als Bean exponiert, sondern per ComponentScan. Auch einige Dependencies der Implementierung werden jetzt geautowired. Aus:

Code:
public class EntityManagerImpl implements EntityManager {

	private Dependency dependency;

	public void save(final Entity anEntity) {
		// save it
	}
}

wurde also:

Code:
@Component
public class EntityManagerImpl implements EntityManager {

	@Autowired private Dependency dependency;

	public void save(final Entity anEntity) {
		// save it
	}
}

Aber das sollte doch komplett irrelevant sein.

Jetzt habe ich in der ConsumingClass also nur noch den Fallback-Block, der die vom Aspekt geworfene Exception fängt und nur sehr rudimentär behandelt. Das ist doof.

Was in der Zwischenzeit passiert ist:
- Implementierung kommt jetzt anders in den Spring-Container

Was nicht passiert ist
- die Signatur der Methode im Interface und in der Implementierung wurden nicht angefasst
- der Aspekt wurde nicht angefasst
- Die Paketstruktur ist gleich geblieben
- Die ConsumingClass hat sich, bis auf den weggefallenen catch-Block, nicht geändert
- Das Java-Target in der pom ist nachwievor 1.8

Ich werde zwar am Montag meine Kollegen dazu fragen, aber es wurmt mich, dass ich das heute nicht selbst lösen konnte. Irgendetwas übersehe ich. Hat irgendwer einen Einfall, was das sein könnte? Brauche ich Bier, um das sehen zu können? :D
 
@Topic
Kann es sein, dass die Exception Klasse in einer früheren Version so aussah?
Code:
public class AspectRuntimeException extends RuntimeException {}
Und nicht
Code:
public class AspectException extends Exception {}
?
In Java gibt es das Konzept von "Checked Exceptions" gegenüber "Unchecked Exceptions"
https://en.wikipedia.org/wiki/Exception_handling#Checked_exceptions
Code:
Exception
an sich ist "checked", d.h. eine Methode die eine Exception (oder davon abstrahierte Klasse wie "AbstractException" werfen möchte, muss das in der Signatur angeben.
Code:
public void save(Entity anEntity) throws Exception;
Und nur dann kann man die auch fangen. Ohne die Angabe an der Methode, kann ja auch keine "checked" Exception geworfen werden, daher wäre ein "catch" Block "unreachable".

Die Ausnahme sind "Unchecked Exception", i.e.
Code:
RuntimeException
und Unterklassen davon. Die müssen nicht in der Methode mit angegen werden und können überall geworfen werden. Daher kann man sie auch immer fangen. Könnte ja sein, dass irgendjemand irgendwo sowas produziert.

@OffTopic
ComputerBase ist nicht die beste Seite um Fragen rund ums Programmieren zu beantworten.
Na gut, es ist auch keine wirklich gute Seite um irgendwelche Fragen beantwortet zu bekommen...
Wieso nicht auf https://stackoverflow.com/ nachfragen?
 
Nein, meine spezielle Exception hat seit jeher von der "normalen" Exception geerbt, deswegen bin ich ja auch so verwirrt. Der Unterschied zwischen checked und unchecked ist mir auch klar. Wenn ich im git vergleiche, ist alles so wie vorher, außer die Reaktion des Compilers, wenn ich ihm mit dem catch-Block komme.

Ist es möglich, dass ich irgendwie Versions-Abfuck kriege, wenn das Hauptprojekt und eine legacy Dependency von uns im selben Paket eine Exception-Klasse gleichen Names definiert? Also eigentlich glaube ich nicht, dass wir zwei verschiedene "AspectException"s haben, aber ganz sicher bin ich mir bei der gewachsenen History nicht. :/

Aber eigentlich hat unser Build-Prozess in Maven einen Schritt, der konkurrierende Dependencies erkennt.

Tante Edith:
Die pragmatische Lösung wird sein, einfach die AspectException als throws zu deklarieren, aber dennoch wüsste ich gerne, woher dieser Unterschied zu vorher rührt. Ich will nicht dumm sterben. :/

Edith Nr 2: Bevor ich stackoverflow frage, frage ich mal meinen Kollegen, ob der sich noch erinnern kann.
 
Zuletzt bearbeitet:
Ich konnte das Problem mit einem Kollegen erleuchten. Im try-Block war noch mehr enthalten, was ich aus Einfachheitsgründen hier weggelassen hatte. Dort wurde im gleichen Commit eine throws-Deklaration entfernt.

Bei Exceptions scheint es also wie folgt zu funktionieren: wenn keine "throws Exception"-Deklaration angegeben ist, dann kann ich nur Exception selbst und unchecked Exceptions fangen. Sobald ich aber "throws Exception" deklariere, kann ich alle von Exception abgeleiteten Exception-Klassen fangen. Das macht im Sinne von Vererbungs- und Polymorphiekram und so eigentlich auch Sinn. :)

Auf jeden Fall ist das Problem jetzt gelöst. Und mein Verständnis für Exceptions ist gewachsen.
 
"Ich konnte das Problem mit einem Kollegen erleuchten. Im try-Block war noch mehr enthalten, was ich aus Einfachheitsgründen hier weggelassen hatte." --> *facepalm*

greetz
hroessler
 
Zurück
Oben