Autorenarchiv

Symbole zu Methoden von Objekten

Freitag, 27. November 2009

Bei der Programmierung eines Suchalgorithmus in Rails habe ich bei der Methode Enumerable#find einen einfachen Weg gesucht, die optionale Callback-Methode _ifnone_ zu setzen.

Die Intention des Algorithmus ist, falls es kein passendes Element (item) zur gestellten Suchbedingung gibt (item search condition), ein bestimmtes (hier: das letzte) Element zurückzugeben, statt nil.

Dafür bin ich unter Funktionale Programmierung auf den folgenden für mich sehr hilfreichen Code-Snippet gestoßen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Dieser Ausdruck verhindert eine doppelte Definition der Operatoren [] und []=
unless :test.respond_to?(:[])
  #
  # Operatoren [] und []= zur Klasse Symbol hinzufügen, um Singleton-Methoden
  # von Objekten zu lesen und zu setzen. Lies : als "Methode" und [] als "von".
  # Also bedeutet zum Beispiel :m[o] "Methode m von o".
  #
  class Symbol
    # Die durch dieses Symbol benannte Methode von obj zurückgeben. Dies kann
    # eine Singleton-Mehtode von obj (etwa eine Klassenmethode) oder eine Instanz-
    # methode sein, die in obj.class definiert oder von einer Oberklasse übernommen
    # wurde.
    # Beispiele:
    #   creator = :new[Object]  #Klassenmethode Object.new
    #   doubler = :*[2]           # *-Methode der Fixnum 2
    #
    def [](obj)
      obj.method self
    end
 
    # Eine Singleton-Methode für Objekt o definieren und Proc oder Method f
    # als Rumpf verwenden. Dieses Symbol dient als Name der Methode.
    # Beispiele:
    #   :singleton[o] = lamba { puts "Dies ist eine Singleton-Methode von o" }
    #   :class_method[String] = lamba { puts "Dies ist eine Klassenmethode" }
    #
    # Beachten: Sie können so keine Instanzmethoden erzeugen. Siehe Module.[]=
    #
    def []=(o,f)
      # self einer Variablen zuweisen, da der folgende Block einen anderen Kontext
      # haben wird.
      sym = self
      # Dies ist das Objekt, für das wir Singleton-Methoden definieren.
      eigenclass = (class << o; self end)
      # define_method ist privat, so dass wir instance_eval verwenden müssen.
      eigenclass.instance_eval { define_method(sym, f) }
    end
  end
end

Mein Anwendungsfall sieht dann folgendermaßen aus:

34
35
36
 ...
 valuearray.find(:last[valuearray]) {|item| /* item search condition */ }
 ...

Als Fallback für eine nicht erfolgreiche Suche verwende ich :last[valuearray], die last-Methode von dem Array-Objekt valuearray.

Auch wenn dieses Beispiel für den durchschnittlichen Ruby-Entwickler etwas überzogen daherkommt,
soll es die Aufmerksamkeit für diesen Themenkomplex der Programmiersprache anregen und die
Vielseitigkeit und Flexibilität von Ruby zeigen.


Hendrik Lange


(De-)Serialisation von Objekten in einen String (Base64 encoded)

Dienstag, 23. Juni 2009

Da es in Java keine direkte Möglichkeit gibt Binärdaten in einen String zu schreiben, kann man hierzu auf die Apache Commons Bibliothek codec zurück greifen.

Diese bietet unteranderem auch eine BASE64-Konvertierung an, die die Datenmenge zwar um ca. 33% größer werden, dann aber problemlos als Zeichenkette weiterverarbeiten lässt.

Im folgenden Beispiel wird ein konkreter Anwendungsfall gezeigt, im dem ein Objekt der Klasse SerienTerminForm mittels ObjectOutputStream serialisiert wird, bevor aus den BASE64-kodierten Daten ein neuer String erzeugt wird.

Natürlich lässt sich die Objekt-Serialisierung auch auf jede andere Klasse anpassen, die die Schnittstelle Serializable implementiert.

        /**
         * @param form
         *            the form data to serialize
         * @return the serialized string (Base64 encoded)
         */
        public static String serializeInfo(final SerienTerminForm form) {
            try {
                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
                final ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(form);
                final String info = new String(new Base64().encode(baos.toByteArray()));
                return info;
            } catch (Exception e) {
                LOGGER.warn("SerienTerminForm not serializable");
                return null;
            }
        }

Hier nun auch der Rückweg aus den in der Zeichenkette gespeicherten Objektinformationen wieder eine lauffähige Instanz zu machen. In diesem Beispiel werden auftretende Ausnahmen nur abgefangen und geloggt, aber immer eine Objekt-Instanz erzeugt.

        /**
         * @param info
         *            the serialized string (Base64 encoded)
         * @return a {@link SerienTerminForm} instance (<code>null</code>-safe)
         */
        public static SerienTerminForm deserializeInfo(final String info) {
            SerienTerminForm form;
            try {
                byte[] bytes = new Base64().decode(info.getBytes());
                final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
                form = (SerienTerminForm) ois.readObject();
            } catch (Exception e) {
                LOGGER.warn("SerienTerminForm not deserializable", e);
                form = new SerienTerminForm();
            }
            return form;
        }

Hendrik Lange


Klassenvergleich bei der Verwendung von CGLIB

Montag, 09. Juni 2008

Wird die Java Bibliothek CGLIB zur dynamischen Kapselung von Klassen verwendet, kann es dazu kommen, dass beim Vergleich der Klassen zweier Instanzen ein Fehler auftritt.

Beispiel soll hier eine überschriebene Equals-Methode sein:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public boolean equals(Object obj) {
    boolean isEqual = false;
    if (obj == null) {
        isEqual = false;
    } else if (this == obj) {
        isEqual = true;
    } else if (!(obj instanceof Superklasse)) {
        isEqual = false;
    /* Problematischer Vergleich */
    } else if (!this.getClass().equals(obj.getClass())) {
        isEqual = false;
    } else {
/* weitere Tests, z.B. mit einem EqualsBuilder */
    }
    return isEqual;
}

Der direkte Klassevergleich funtioniert nun nicht mehr, da es sich bei obj um eine Instanz der CGLIB-generierten Subklasse handelt.

Ein Möglichkeit ist die Aufweichung der Klassenbedingung:

!this.getClass().isAssignableFrom(obj.getClass())

Eine bessere die Verwendung der ClassUtils aus dem Spring Framework:

!this.getClass().equals(ClassUtils.getUserClass(obj))

Ersatzweise hier auch der benutzte Code aus der ClassUtils-Klasse:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class ClassUtils {
    /** The CGLIB class separator character "$$" */
    public static final String CGLIB_CLASS_SEPARATOR = "$$";
 
    /**
     * Return the user-defined class for the given instance: usually simply
     * the class of the given instance, but the original class in case of a
     * CGLIB-generated subclass.
     * @param instance the instance to check
     * @return the user-defined class
     */
    public static Class getUserClass(Object instance) {
        return getUserClass(instance.getClass());
    }
 
    /**
     * Return the user-defined class for the given class: usually simply the given
     * class, but the original class in case of a CGLIB-generated subclass.
     * @param clazz the class to check
     * @return the user-defined class
     */
    public static Class getUserClass(Class clazz) {
        return (clazz != null && clazz.getName().indexOf(CGLIB_CLASS_SEPARATOR) != -1 ?
                clazz.getSuperclass() : clazz);
    }
 
}

Hendrik Lange


Sonderzeichen in Resource Bundles

Mittwoch, 09. April 2008

Die Werte aus den properties-Dateien werden mit einer MessageFormat Instanz mit möglichen Argumenten befüllt. Die Position, die Selection des Argumentes und zusätzliche Formatierungen müssen in den Text integriert werden.

Die einfachste Angabe ist {0} für das erste mitgegebene Argument.

Deshalb werden die Zeichen ‘ (quote), \ (backslash), { (open brace) und } (close brace) als Sonderzeichen behandelt und müssen für die normale Verwendung folgendermaßen verschlüsselt werden, um sie vor dem Parser zu schützen:

  • ein ‘ wird mit einem weiteren ‘ versehen, so dass ein ” (double-single quote) entsteht
  • ein \ wird mit einem weiteren \ versehen, so dass ein \ \ (double backslash) entsteht
  • ein } wird mit ‘ umfasst, so dass die Zeichenkette ‘}’ entsteht
  • ein { wird mit ‘ umfasst, so dass die Zeichenkette ‘{’ entsteht

Referenz: Java-API, siehe MessageFormat

Hinweis: Am besten einen Resource Bundle Editor verwenden z.B. für eclipse: Resource Bundle Editor
Dann werden auch Umlaute und Sonderzeichen in Unicode gewandelt.


Hendrik Lange