Man versucht immer, Klassen so unabhängig wie möglich zu halten.
Und mit Hilfe von Abstraktionen kann man hier einiges an Unabhängigkeit erreichen.
Dies erreicht man z.B. über Interfaces. Dir ist egal, was für ein Auto du fährst - es muss aber das Interface AutoMitSchaltgetriebe haben.
Dieser erste Schritt Klassen unabhängig zu machen, reicht aber leider nicht ganz. Das ist super, denn eine Instanz fest auf eine andere zugreifen will. Aber nun kann es sein, dass es weitere Klassen gibt, die noch gar nicht bekannt sind. Wenn das Auto einen Unfall hat, dann soll irgend etwas automatisch passieren. Was genau weiss man aber nicht, wenn man das Auto baut. Das weisst auch Du nicht, der das Auto später baut. Also baut man das einfach als eine universelle Sache ein. Man schafft ein Interface, das für die Benachrichtigung da ist. Und dann darf sich jeder, der an dem Ereignis interessiert ist, anmelden um dann über das Ereignis informiert zu werden.
Nichts anderes ist das mit den Events. Ein Event ist einfach ein Ereignis. In einer Oberfläche kann es sein; Der Anwender hat auf etwas geklickt, hat eine Taste gedrückt oder oder oder ....
Das nennt sich dann Event. Event ist also ein Ereignis.
Ein Listener ist jemand, der dann dem Event interessiert ist. Dieser muss in der Regel ein bestimmtes Interface erfüllen. Halt das Interface, das notwendig ist, um das Event entgegen zu nehmen.
In dem Aufruf gibt es dann natürlich notwendige Daten. Auto baut Unfall -> Da sind dann Daten wichtig, z.B. genaue Koordinaten, welches Auto es war u.s.w. Diese Informationen sind bei jedem Event durchaus unterschiedlich. In einer UI hat ein Event aber einen Ursprung (source). Und diesen Ursprung kann man abfragen. Das kann aber vom Prinzip (fast) alles sein, daher kann hier nur ein universeller Typ genommen werden. Das kann z.B. Object sein. Damit Du mit der Referenz etwas anfangen kannst, musst du natürlich in den eigentlichen Typ zurück gehen. Du machst also ein Cast ist das, was der Ursprung war. Das funktioniert aber natürlich nur, wenn das auch stimmt. Wenn der Ursprung des Events ein MenuItem war und Du willst es nach Button casten: Das geht nicht. Dann bekommst Du eine ClassCastException.
Klar - wenn Ursprung ein Auto ist und Du willst es als Fahrrad nutzen: Das geht nicht.
Bezüglich Adapterklassen ist der Kontext nicht unwichtig. Ich kenne Adapterklassen eigentlich als Klassen, die andere Instanzen umwandeln. Du hast also einen Adapter um Transferobjekte entgegen zu nehmen und deine eigenen Entities auszugeben und umgekehrt.
Aber
https://javabeginners.de/Klassen_und_Interfaces/Adapterklassen.php beschreibt auch eine andere Art von Adapterklasse, die ich irgendwie sehr kritisch sehe. Evtl. meinst Du dies. Da geht es um Interfaces mit mehreren Methoden und dann abstrakten Klassen, die das Interface implementieren und die dann statt Interface verwendet werden können. Diese Klassen haben eine leere Methode für jede vom Interface angeforderte Methode und sollen dazu dienen, dass man nicht alle Methoden implementieren muss.
Warum sehe ich das kritisch: Es ist sehr wichtig, sauberen, wartbaren Code zu schreiben, so genannten Clean Code. Und es gibt in dem Bereich sehr viele Überlegungen. Stark duchgesetzt hat sich u.a. "Uncle Bob" (Robert C.Martin - Autor diverers Bücher, hat gute Videos auf Youtube!) der massgeblich an den SOLID Principles beteiligt war. Und ein Principle ist "Interface segregation". Interfaces sollten so klein sein, dass eben diese Notwendigkeit nicht mehr bestehen kann!
Du hast ein Interface I mit den Methoden a und b. Das setzt voraus, dass die Methoden a und b so zusammen hängen, dass diese nicht einzeln Sinn ergeben. Wenn es Sinn machen kann, a und b zu Trennen, dann macht es auch Sinn, das Interface aufzutrennen. Dann hast Du Ia mit a und Ib mit b.
Beispiel:
Eine Ente kann quaken und schwimmen. Jetzt hast Du etwas, das kann nur Schwimmen. Dann ist es aber ggf. keine Ente ... Also macht man lieber zwei Interfaces KannQuaken und KannSchwimmen.