Olá pessoal! Estou com um conflito entre a inicialização do objeto e a inicialização reflexiva de atributos final.
É o seguinte:
Estou fazendo uma espécie de DAO voltado para programadores oracle, ou seja: ele terá uma sintaxe bem similar à DML. Para conseguir a flexibilidade necessária para gerar selects complexos, precisei criar um Pojo inteligente (DbTable) que se encarrega de carregar toda a infraestrutura de mapeamento com as tabelas do banco. Os “Beans” serão filhos da DbTable e terão que declarar suas colunas como objetos TableColumn anotados.
Para assegurar que os selects desse DAO sejam feitos de forma muito similar à da DML, fiz que esses atributos fossem publicos e finais. Nota: esses atributos representam colunas e naõ dados.
Devido aos atributos de coluna serem publicos, eu os tornei final p/ evitar problemas. Porém ao fazer isso, o java me obriga a inicializar a variável (sendo assim, atribuo null nela).
O problema aparece quando vou inicializar meu “Bean”. Pela pilha de inicialização, o java chama o super do construtor (DbTable) que é encarregado de instanciar os atributos TableColumn da classe filha via reflexão. Porém ao executar o construtor da classe (Client), essa anula o TableColumn instanciado pela DbTable!
Seguem abaixo os códigos que exemplificam meu problema (Eu simplifiquei ao máximo as classes apenas para evidenciar o problema em si).
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
String name();
}
public class TableColumn {
public static TableColumn Integer(DbTable table, String name) {
return new TableColumn(table, name);
}
private final DbTable table;
private final String name;
private TableColumn(DbTable table, String name) {
this.table = table;
this.name = name;
}
}
import java.lang.reflect.Field;
public abstract class DbTable {
private String tableName = this.getClass().getSimpleName();
private String alias = this.tableName;
protected DbTable() throws Exception {
System.out.println("Inicio de DbTable()...");
loadColumns();
}
private TableColumn instancializeColumns(Field field) throws Exception {
Column column = field.getAnnotation(Column.class);
TableColumn tableColumn = TableColumn.Integer(this, column.name());
field.set(this, tableColumn);
return tableColumn;
}
protected void loadColumns() throws Exception {
for (Field field : this.getClass().getFields()) {
if (field.getType() == TableColumn.class) {
field.setAccessible(true);
TableColumn column = (TableColumn) field.get(this);
if (column == null) column = instancializeColumns(field);
System.out.format("DbTable() Instanciou o atributo %s com [%s]\n", field.getName(), field.get(this));
}
}
}
}
public class Clients extends DbTable {
@Column(name = "id")
public final TableColumn id = null;
public Clients() throws Exception {
super();
System.out.format("Clients() Instanciou o atributo %s com [%s]\n", "id", this.id);
}
}
public class Main {
public static void main(String[] args) throws Exception {
Clients c = new Clients();
System.out.println("ID após o termino da inicialização: " + c.id);
}
}
Saída:
Não consegui descobrir como modificar um atributo para final via reflexão, se alguém souber como fazer, seria muito útil.
Transformar os objetos TableColumn em static também naõ ajudará, pois isso causará problemas para referenciar as colunas do select com as tabelas envolvidas.
Alguém poderia me ajudar? Até com idéias mirabolantes de magia negra?