Yes, Java has them. They are called annotations. Finding a complete example is hard to find on the web. But this is how you use them to give a real-world example of reading and parsing TIGER EDGE DBF files using geotools library.
import org.geotools.data.shapefile.dbf.DbaseFileReader;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
@Retention(value=RetentionPolicy.RUNTIME)
public @interface Column {
int value() default -1;
}
public class TigerEdge {
@Column(0)
private String stateFP;
@Column(1)
private String countyFP;
@Column(2)
private long tlid;
@Column(3)
private long tfidl;
@Column(4)
private long tfidr;
@Column(5)
private String mtfcc;
@Column(6)
private String fullName;
@Column(7)
private String smid;
@Column(8)
private String lfromadd;
@Column(9)
private String ltoadd;
@Column(10)
private String rfromadd;
@Column(11)
private String rtoadd;
@Column(12)
private String zipl;
@Column(13)
private String zipr;
@Column(14)
private String featcat;
@Column(15)
private String hydroflg;
@Column(16)
private String railflg;
@Column(17)
private String roadflg;
@Column(18)
private String olfflg;
@Column(19)
private String passflg;
@Column(20)
private String divroad;
@Column(21)
private String exttyp;
@Column(22)
private String ttyp;
@Column(23)
private String deckedroad;
@Column(24)
private String artpath;
@Column(25)
private String persist;
@Column(26)
private String gcseflg;
@Column(27)
private String offsetl;
@Column(28)
private String offsetr;
@Column(29)
private long tnidf;
@Column(30)
private long tnidt;
private TigerEdge() {}
public static TigerEdge fromDbf(DbaseFileReader.Row row) throws Exception
{
return (TigerEdge) DbfParser.parseDbf(row, TigerEdge.class);
}
}
import org.geotools.data.shapefile.dbf.DbaseFileReader;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
@Retention(value= RetentionPolicy.RUNTIME)
@interface Column {
int value() default -1;
}
public class DbfParser {
public static Object parseDbf(DbaseFileReader.Row row, Class c) throws Exception
{
// http://stackoverflow.com/questions/8249173/what-is-the-difference-between-getdeclaredconstructors-and-getconstructors-in-th
Constructor ctor = c.getDeclaredConstructor();
ctor.setAccessible(true);
Object answer = ctor.newInstance();
Field[] allFields = c.getDeclaredFields();
for (Field field : allFields) {
if (Modifier.isPrivate(field.getModifiers())) {
// without setting the fields to be accessible, there will be an error trying to get/set it
field.setAccessible(true);
Type t = field.getType();
int column = field.getAnnotation(Column.class).value();
Object o = row.read(column);
if (t == String.class && o.getClass() == String.class)
{
field.set(answer, o);
}
else if (t == long.class)
{
if (o.getClass() == Long.class) {
field.setLong(answer, (long) o);
}
else if (o.getClass() == Double.class)
{
// normally o.class MUST be equal to Long.class
// but looks like due to a bug in DbaseFileReader
// 0 is misinterpreted as 0.0 and we need a special check
// to take care of it
field.setLong(answer, ((Double)o).longValue());
}
else
{
String message = String.format("unable to parse %s expected a long or double", o);
throw new IllegalArgumentException(message);
}
}
else
{
throw new IllegalStateException();
}
}
}
return answer;
}
}