Table of Contents

Java Beans

Introducción

Los Java Beans -en castellano granos de café java- no son otra cosa que un modelo de componentes para Java. Se estandariza la forma de definir las propiedades (métodos get y set), cómo deben notificarse cambios en los estados del componente (propiedades ligadas, constraints) y cómo acceder a colecciones de elementos (propiedades índice).

Estas notas están sacadas del Tutorial de Java sobre java beans y he extraido aquello que se refiere sólamente al código fuente, dejando de lado los aspectos de diseño y visuales.

Propiedades sencilla

Un java bean sencillo:

public class MyBean {
 
    /** Creates a new instance of MyBean */
    public MyBean() {
    }
 
    /**
     * Holds value of property yourName.
     */
    private String yourName;
 
    /**
     * Getter for property yourName.
     * @return Value of property yourName.
     */
    public String getYourName() {
        return this.yourName;
    }
 
    /**
     * Setter for property yourName.
     * @param yourName New value of property yourName.
     */
    public void setYourName(String yourName) {
        this.yourName = yourName;
    }
 
}

Las propiedades exponen dos métodos públicos: getNombrePropiedad y setNombrePropiedad. El “set” siempre retorna void, el get siempre retorna el tipo de la propiedad.

Propiedades Indexadas

Cuando una propiedad debe exponer una colección de elementos (el acceso a una lista de valores), los métodos a programar son cuatro:

    /**
     * Holds value of property lines.
     */
    private String[] lines;
 
    /**
     * Indexed getter for property lines.
     * @param index Index of the property.
     * @return Value of the property at index.
     */
    public String getLines(int index) {
        return this.lines[index];
    }
 
    /**
     * Getter for property lines.
     * @return Value of property lines.
     */
    public String[] getLines() {
        return this.lines;
    }
 
    /**
     * Indexed setter for property lines.
     * @param index Index of the property.
     * @param lines New value of the property at index.
     */
    public void setLines(int index, String lines) {
        this.lines[index] = lines;
    }
 
    /**
     * Setter for property lines.
     * @param lines New value of property lines.
     */
    public void setLines(String[] lines) {
        this.lines = lines;
    }

Propiedades Ligadas

Lo que haremos en este caso es utilizar un objeto PropertyChangeListener para que cada vez que una de las propiedades cambie, se mande un mensaje a aquellos objetos que se hayan registrado como “listeners 1)” de nuestro bean.

import java.awt.Graphics;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import javax.swing.JComponent;
 
/**
 * Bean with bound properties.
 */
public class MyBean
        extends JComponent
        implements Serializable
{
    private String title;
    private String[] lines = new String[10];
 
    private final PropertyChangeSupport pcs = new PropertyChangeSupport( this );
 
    public String getTitle()
    {
        return this.title;
    }
 
    public void setTitle( String title )
    {
        String old = this.title;
        this.title = title;
        this.pcs.firePropertyChange( "title", old, title );
    }
 
    public String[] getLines()
    {
        return this.lines.clone();
    }
 
    public String getLines( int index )
    {
        return this.lines[index];
    }
 
    public void setLines( String[] lines )
    {
        String[] old = this.lines;
        this.lines = lines;
        this.pcs.firePropertyChange( "lines", old, lines );
    }
 
    public void setLines( int index, String line )
    {
        String old = this.lines[index];
        this.lines[index] = line;
        this.pcs.fireIndexedPropertyChange( "lines", index, old, lines );
    }
 
    public void addPropertyChangeListener( PropertyChangeListener listener )
    {
        this.pcs.addPropertyChangeListener( listener );
    }
 
    public void removePropertyChangeListener( PropertyChangeListener listener )
    {
        this.pcs.removePropertyChangeListener( listener );
    }
 
}

Propiedades ligadas y limitadas

Existe también la posibilidad de que los listeners puedan “vetar” un cambio, que son lo que se llama “constrained properties” 2).

import java.io.Serializable;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.awt.Graphics;
import javax.swing.JComponent;
 
/**
 * Bean with constrained properties.
 */
public class MyBean
        extends JComponent
        implements Serializable
{
    private String title;
    private String[] lines = new String[10];
 
    private final PropertyChangeSupport pcs = new PropertyChangeSupport( this );
    private final VetoableChangeSupport vcs = new VetoableChangeSupport( this );
 
    public String getTitle()
    {
        return this.title;
    }
/**
 * This method was modified to throw the PropertyVetoException
 * if some vetoable listeners reject the new title value
 */
    public void setTitle( String title )
            throws PropertyVetoException
    {
        String old = this.title;
        this.vcs.fireVetoableChange( "title", old, title );
        this.title = title;
        this.pcs.firePropertyChange( "title", old, title );
    }
 
    public String[] getLines()
    {
        return this.lines.clone();
    }
 
    public String getLines( int index )
    {
        return this.lines[index];
    }
/**
 * This method throws the PropertyVetoException
 * if some vetoable listeners reject the new lines value
 */
    public void setLines( String[] lines )
            throws PropertyVetoException
    {
        String[] old = this.lines;
        this.vcs.fireVetoableChange( "lines", old, lines );
        this.lines = lines;
        this.pcs.firePropertyChange( "lines", old, lines );
    }
 
    public void setLines( int index, String line )
            throws PropertyVetoException
    {
        String old = this.lines[index];
        this.vcs.fireVetoableChange( "lines", old, line );
        this.lines[index] = line;
        this.pcs.fireIndexedPropertyChange( "lines", index, old, line );
    }
 
    public void addPropertyChangeListener( PropertyChangeListener listener )
    {
        this.pcs.addPropertyChangeListener( listener );
    }
 
    public void removePropertyChangeListener( PropertyChangeListener listener )
    {
        this.pcs.removePropertyChangeListener( listener );
    }
/**
 * Registration of the VetoableChangeListener
 */
    public void addVetoableChangeListener( VetoableChangeListener listener )
    {
        this.vcs.addVetoableChangeListener( listener );
    }
 
    public void removeVetoableChangeListener( VetoableChangeListener listener )
    {
        this.vcs.removeVetoableChangeListener( listener );
    }
 
    protected void paintComponent( Graphics g )
    {
        g.setColor( getForeground() );
 
        int height = g.getFontMetrics().getHeight();
        paintString( g, this.title, height );
 
        if ( this.lines != null )
        {
            int step = height;
            for ( String line : this.lines )
                paintString( g, line, height += step );
        }
    }
 
    private void paintString( Graphics g, String str, int height )
    {
        if ( str != null )
            g.drawString( str, 0, height );
    }
}

Persistencia: la vida más allá de la cpu

Hay dos modos de que un java bean pueda alcanzar la persistencia: serialización y externalización.

La serialización se consigue implentando el interfaz Serializable, que podemos entender como un mero “marcador” que indica que nuestro componente es puede serializarse y por lo tanto ser almacenado en disco –o transferido por la red–.

Si la clase padre ya implementa Serializable, entonces la nuestra es también serializable.

Si queremos que una clase o método no se guarde en el proceso de serialización, usaremos el marcador “transient”.

Podemos hacer que la serialización no funcione por defecto implementando estos dos métodos en nuestra clase:

private void writeObject( java.io.ObjectOutputStream out) throws IOException; 
 
private void readObject( java.io.ObjectInputStream out) throws IOException, ClassNotFoundException;

La externalización exige de un mayor trabajo por nuestra parte, y está pensada para que el contenido del java bean se escriba en disco.

Se hace básicamente heredando el interfaz Externalizable e implementando a continuación los métodos readExternal y writeExternal.

La clase que implemente Externalizable tiene que tener un constructor sin argumentos.

Codificando y descodificando un objeto en XML

La clase XMLEncoder permite hacer esto. Ahí van dos ejemplos:

XMLEncoder e = new XMLEncoder(
    new BufferedOutputStream(
        new FileOutputStream("Test.xml")));
e.writeObject(object);
e.close()
XMLDecoder decoder = new XMLDecoder( 
              new BufferedInputStream( 
              new FileInputStream( "Test.xml" ) ) ); 
Object object = decoder.readObject(); 
decoder.close();
1)
Oyentes
2)
propiedades limitadas