CroftSoft /
Library /
Tutorials
Enumerated Accessors
David Wallace Croft
2007 Jun 29 Fri
Abstract.
Adding new data fields to a Java class can be simplified by using enumerated
types as arguments to accessor and mutator methods.
This tutorial is an update of the "Persistent Data" chapter in my book
Advanced Java Game Programming. Data management and persistence are
simplified by using new features that have been added to the Java
programming language such as enumerated types and JAXB.
package com.croftsoft.apps.ajgp.data;
/*********************************************************************
* Example of enumerated accessors.
*
* @since
* 2007-06-28
* @author
* <a href="https://www.croftsoft.com/">David Wallace Croft</a>
*********************************************************************/
public interface EnumData
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
{
public enum EnumInteger
{
HEALTH,
WEALTH,
WISDOM,
}
public enum EnumString
{
CHARACTER_NAME,
PLAYER_NAME,
USER_ID,
}
////////////////////////////////////////////////////////////////////////
// accessor methods
////////////////////////////////////////////////////////////////////////
public Integer get ( EnumInteger enumInteger );
public String get ( EnumString enumString );
////////////////////////////////////////////////////////////////////////
// mutator methods
////////////////////////////////////////////////////////////////////////
public void set (
EnumInteger enumInteger,
Integer value );
public void set (
EnumString enumString,
String value );
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
}
Please note in the preceding code listing for interface EnumData that the
accessor and mutator methods (getters and setters) have enumerated type
arguments. Instead of calling getHealth(), one calls
get(EnumInteger.HEALTH). Adding new data fields as your program grows is as
simple as adding a new constant to the list of enumerated types.
Strong compile-time typing is ensured since the enumerated types are grouped
by data type.
package com.croftsoft.apps.ajgp.data;
import java.util.*;
import javax.xml.bind.annotation.*;
import com.croftsoft.core.lang.NullArgumentException;
/*********************************************************************
* Bean implementation of interface of EnumData.
*
* @since
* 2007-06-28
* @author
* <a href="https://www.croftsoft.com/">David Wallace Croft</a>
*********************************************************************/
@XmlRootElement
public final class EnumDataBean
implements EnumData
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
{
private Map<EnumInteger, Integer> integerMap;
private Map<EnumString, String> stringMap;
////////////////////////////////////////////////////////////////////////
// constructor methods
////////////////////////////////////////////////////////////////////////
public EnumDataBean ( )
////////////////////////////////////////////////////////////////////////
{
// a no-argument constructor for JAXB conversion
}
////////////////////////////////////////////////////////////////////////
// interface EnumData accessor methods
////////////////////////////////////////////////////////////////////////
public Integer get ( final EnumInteger enumInteger )
////////////////////////////////////////////////////////////////////////
{
NullArgumentException.check ( enumInteger );
return integerMap == null ? null : integerMap.get ( enumInteger );
}
public String get ( final EnumString enumString )
////////////////////////////////////////////////////////////////////////
{
NullArgumentException.check ( enumString );
return stringMap == null ? null : stringMap.get ( enumString );
}
////////////////////////////////////////////////////////////////////////
// interface EnumData mutator methods
////////////////////////////////////////////////////////////////////////
public void set (
final EnumInteger enumInteger,
final Integer value )
////////////////////////////////////////////////////////////////////////
{
NullArgumentException.check ( enumInteger );
if ( integerMap == null )
{
integerMap
= new EnumMap<EnumInteger, Integer> ( EnumInteger.class );
}
if ( value == null )
{
integerMap.remove ( enumInteger );
}
else
{
integerMap.put ( enumInteger, value );
}
}
public void set (
final EnumString enumString,
final String value )
////////////////////////////////////////////////////////////////////////
{
NullArgumentException.check ( enumString );
if ( stringMap == null )
{
stringMap = new EnumMap<EnumString, String> ( EnumString.class );
}
if ( value == null )
{
stringMap.remove ( enumString );
}
else
{
stringMap.put ( enumString, value );
}
}
////////////////////////////////////////////////////////////////////////
// JAXB accessor methods
////////////////////////////////////////////////////////////////////////
public Map<EnumInteger, Integer> getIntegerMap ( )
////////////////////////////////////////////////////////////////////////
{
return integerMap;
}
public Map<EnumString, String> getStringMap ( )
////////////////////////////////////////////////////////////////////////
{
return stringMap;
}
////////////////////////////////////////////////////////////////////////
// JAXB mutator methods
////////////////////////////////////////////////////////////////////////
public void setIntegerMap (
final Map<EnumInteger, Integer> integerMap )
////////////////////////////////////////////////////////////////////////
{
this.integerMap = integerMap;
}
public void setStringMap (
final Map<EnumString, String> stringMap )
////////////////////////////////////////////////////////////////////////
{
this.stringMap = stringMap;
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
}
The preceding code listing for EnumDataBean is a bean implementation of
interface EnumData. A bean implementation requires a no-argument
constructor and a get/set method pair for each data field. In this case
the data fields are integerMap and stringMap. Using a bean implementation
permits automatic conversion to XML using JAXB.
package com.croftsoft.apps.ajgp.data;
import java.io.*;
import javax.xml.bind.*;
import com.croftsoft.apps.ajgp.data.EnumData.*;
/*********************************************************************
* Test of enumerated accessors.
*
* @since
* 2007-06-28
* @author
* <a href="https://www.croftsoft.com/">David Wallace Croft</a>
*********************************************************************/
public final class EnumDataTest
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
{
public static void main ( final String [ ] args )
throws Exception
////////////////////////////////////////////////////////////////////////
{
final EnumData enumData = new EnumDataBean ( );
enumData.set ( EnumInteger.HEALTH, new Integer ( 10 ) );
enumData.set ( EnumInteger.WEALTH, new Integer ( 99 ) );
enumData.set ( EnumInteger.WISDOM, new Integer ( 18 ) );
enumData.set ( EnumString.USER_ID, "croft" );
enumData.set ( EnumString.PLAYER_NAME, "David Wallace Croft" );
enumData.set ( EnumString.CHARACTER_NAME, "Enoch the Elf" );
// convert from bean object to XML
final JAXBContext jaxbContext
= JAXBContext.newInstance ( EnumDataBean.class );
final Marshaller marshaller = jaxbContext.createMarshaller ( );
marshaller.setProperty (
Marshaller.JAXB_FORMATTED_OUTPUT,
Boolean.TRUE );
final ByteArrayOutputStream byteArrayOutputStream
= new ByteArrayOutputStream ( );
marshaller.marshal ( enumData, byteArrayOutputStream );
final byte [ ] byteArray = byteArrayOutputStream.toByteArray ( );
System.out.println ( new String ( byteArray, "UTF-8" ) );
// convert from XML to bean object
final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller ( );
final EnumData enumData2
= ( EnumData ) unmarshaller.unmarshal (
new ByteArrayInputStream ( byteArray ) );
System.out.println ( "" );
for ( final EnumInteger enumInteger : EnumInteger.values ( ) )
{
System.out.println ( enumData2.get ( enumInteger ) );
}
for ( final EnumString enumString : EnumString.values ( ) )
{
System.out.println ( enumData2.get ( enumString ) );
}
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
}
The preceding code listing for EnumDataTest demonstrates setting the data
values via the enumerated type argument mutator methods
(enumerated mutators), automatically converting the bean object into XML,
converting it back from XML into an object again, and iterating over the
data using the enumerated accessors. The following listing shows the
output.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<enumDataBean>
<integerMap>
<entry>
<key>HEALTH</key>
<value>10</value>
</entry>
<entry>
<key>WEALTH</key>
<value>99</value>
</entry>
<entry>
<key>WISDOM</key>
<value>18</value>
</entry>
</integerMap>
<stringMap>
<entry>
<key>CHARACTER_NAME</key>
<value>Enoch the Elf</value>
</entry>
<entry>
<key>PLAYER_NAME</key>
<value>David Wallace Croft</value>
</entry>
<entry>
<key>USER_ID</key>
<value>croft</value>
</entry>
</stringMap>
</enumDataBean>
10
99
18
Enoch the Elf
David Wallace Croft
croft
You can see how as your program grows, this permits you to easily add data
fields with automatic XML persistence simply by adding a new enumerated type
constant to your interface. Instead of a get/set method pair for each data
field such as getHealth()/setHealth(), you now only need to implement a
get/set pair for each data type such as get(EnumInteger)/set(EnumInteger).
Strong compile-time typing is preserved as the return type of an
enumerated accessor is indicated by the enumerated type argument.
|