Archive for September, 2018

JABWT (JSR-82) – Teil 1: Bluetooth Protokolle

Tuesday, September 25th, 2018

JABWT (”Java APIs for Bluetooth wireless technology”) ist der Java-Standard (JSR-82) für Bluetooth Applikationen. Mittlerweile wird dieser Standard von vielen aktuellen Mobiltelefonen unterstützt. In dieser Artikelserie geht es um einen sehr groben Überblick über die Möglichkeiten dieser API.

Als Einstieg kurz die wichtigsten Bluetooth Protokolle :

Bluetooth Protokolle: L2CAP, RFCOMM, OBEX und SDP

L2CAP (Logical Link Control and Adaptation Protocol) – Jede Bluetooth Datenverbindung (Ausnahme: Bluetooth Audio) verwendet das L2CAP Protokoll als Basis. Dieses Protokoll verwendet das HCI (Host Controller Interface) und abstrahiert den eigentlichen Zugriff auf den Bluetooth Controller.

RFCOMM (Radio Frequency Communication) simuliert eine oder mehrere RS-232 Serialport Verbindungen zwischen zwei Geräten. Die Daten werden in Form von Streams ausgetauscht.

OBEX (Object Exchange Protokoll) baut auf RFCOMM auf und ist als Alternative zu HTTP konzipiert worden. Ein OBEX Request (und auch die Response) kann in kleinere Teile zerlegt und verarbeitet werden. Einsatzzweck ist z.B. das Drucken von Dokumenten (Das Basic Printing Profile basiert auf OBEX)

SDP (Service Discovery Protokoll) erlaubt das Suchen und Inspizieren von Bluetooth Diensten.

Generics Teil 3: Wildcards (und lesender Zugriff darauf)

Thursday, September 20th, 2018

Auch der lesende Zugriff auf Wildcard Referenzen ist eingeschränkt.

In einer ArrayList<? extends E> werden Objekte vom Typ E oder Subtypen von E gespeichert.
Da wir die obere Grenze kennen, können wir bedenkenlos die Supertypen von E (oder E selbst) aus dieser ArrayList lesen.

Beispiel:

  1. ArrayList<String> stringList = new ArrayList<String>();
  2. stringList.add(new String());
  3. ArrayList<? extends CharSequence> list = stringList ;
  4. CharSequence cs = list.get(0); // gibt ein Objekt vom Typ CharSequence zurueck.

In einer ArrayList<strong><? super E> werden Objekte vom Typ E oder die Supertypen von E gespeichert. Hier kennen wir nur die untere Grenze, und der einzige Cast, der immer funktioniert, ist der Cast nach Object. Von daher gibt ein lesender Zugriff immer ein Objekt vom Typ Object zurück.

Beispiel:

  1. ArrayList<CharSequence> csList = new ArrayList<CharSequence>();
  2. csList .add(new String());
  3. ArrayList<? super String> list = csList  ;
  4. Object o = list.get(0); // gibt ein Objekt vom Typ Object zurueck.

Ok, so weit zur Theorie. Aber wo werden diese Wildcards konkret eingesetzt ? Google hilft.

<? extends E> wird in der Klassenbibliothek 25 mal eingesetzt.
Z.B. Collection.addAll(Collection <? extends E> c)

<? super E> dagegen nur 9 mal.
Z.B. im Konstruktor von TreeSet(Comparator<? super E> c)

Generics Teil 2: Wildcards (und schreibender Zugriff darauf)

Monday, September 3rd, 2018

Komplexer werden die Generics mit der Verwendung von sogenannten Wildcards. Es gibt 3 Typen von Wildcards:

  • ArrayList<?> – Die ArrayList kann mit jedem Typ parametisiert worden sein.
  • ArrayList<? super E> – Die ArrayList kann mit Typ E oder einer Oberklasse von Typ E parametisiert worden sein. (E markiert dabei die untere Grenze)
  • ArrayList<? extends E> Die ArrayList kann mit Typ E oder einer Unterklasse von Typ E parametisiert worden sein. (E markiert dabei die obere Grenze)

Jetzt wirds spannend: Der schreibende Zugriff solche Referenzen ist eingeschränkt. Beispielsweise darf ich in eine Wildcard ArrayList<?> nichts hinzufügen. Warum?
Weil nicht bekannt ist, mit welchen Typ diese Liste parametisiert wurde. Wenn ich in die Liste
jeden Typ einfügen könnte, dann könnte ich auch einen Typ einfügen, der nicht zum Parametertyp gecastet werden kann.
Das würde dann eine ClassCastException zur Laufzeit bedeuten. (Und das wäre wiederum unschön).

Beispiel:

  1. ArrayList<?> list = new ArrayList<CharSequence>();
  2. list.add(new String()); // semantisch zwar richtig, aber der Compiler kann das nicht zulassen, da sonst auch
  3. list.add(new Object()); // funktionieren wuerde
  4.  

In eine ArrayList<? super E> kann ich dagegen Typ E oder eine Unterklasse von Typ E einfügen, da garantiert ist, dass die Liste mit einer Oberklasse von Typ E (oder Typ E selbst) parametisiert wurde. Ein Cast nach E wird also immer funktionieren.

Beispiel:

  1. ArrayList<? super CharSequence> list = new ArrayList<Object>();
  2. list.add(new String());   // funktioniert, da String von CharSequence ableitet
  3. // (und so auch nach CharSequence oder hoeher gecastet werden koennte)
  4.  

In eine ArrayList<? extends E> dürfen überhaupt keine Elemente hinzugefügt werden. Warum?
Weil sonst immer die Gefahr einer ClassCastException besteht.

Beispiel:

  1. ArrayList<? extends CharSequence> list = new ArrayList<StringBuffer>();
  2. list.add(new String()); // Compilerfehler. String leitet zwar von CharSequence ab, kann aber nicht nach StringBuffer gecastet werden.
  3.  

So, genug für heute. Im nächsten Eintrag geht es um dem lesenden Zugriff.

Generics Teil 1: Einführung

Sunday, September 2nd, 2018

Vor kurzem wurde ich nach dem Sinn bzw. dem Einsatzzweck von <? super E> gefragt.
Bevor ich die Frage hier im Blog nochmals beantworte, kurz noch eine ganz kleine Einführung in Generics

Mit dem JDK 5.0 kamen die sogenannten Generics, mit denen Klassen und Methoden zusätzlich parametisiert werden können (Ähnlich wie die Templates in C++, mit dem Unterschied, dass keine neuen Klassen pro Parametertyp erstellt werden). Ziel der Generics ist die Lesbarkeit des Codes zu erhöhen und explizite Casts zu reduzieren. Allerdings kann die Verwendung von Generics auch sehr tückisch werden. Aber fangen wir mal einfach an (also ohne Generics):

Der folgende Codeausschnitt wird kompiliert und erzeugt zur Laufzeit eine ClassCastException. Unschön.

  1. ArrayList intList = new ArrayList();
  2. intList.add(new Double(2)); // Semantisch falsch, aber kein Fehler beim Kompilieren.
  3. Integer integer = (Integer) intList.get(0); // Ups, ClassCastException zur Laufzeit

Ungefährlicher ist Codeausschnitt 2, da hier schon der Compiler den Fehler meldet:

  1. ArrayList<Integer> intList = new ArrayList();
  2. intList.add(new Double(2))     // Fehler zur Kompilierzeit
  3. Integer integer = (Integer) intList.get(0);

Mit der Verwendung der Generics wird die ArrayListe parametisiert und erlaubt nur das Hinzufügen von Integer Objekten. Der Compiler unterbindet den fehlerhaften Aufruf und verhindert unliebsame
Überraschungen zur Laufzeit. Schön.

Morgen gibts schon weiter mit Teil 2: Wildcards. Da wirds etwas kniffliger.