tags:

views:

86

answers:

5

After using C++ I got used to the concept of Identifier which can be used with a class for the type, provides type safety and has no runtime overhead (the actual size is the size of the primitive). I want to do something like that, so I will not make mistakes like:

personDao.find(book.getId());//I want compilation to fail  
personDao.find(book.getOwnerId());//I want compilation to succeed

Possible solutuions that I don't like:

  1. For every entity have an entity id class wrapping the id primitive. I don't like the code bloat.
  2. Create a generic Identifier class. Code like this will not compile:

    void foo(Identifier<Book> book);
    void foo(Identifier<Person> person);

Does anyone know of a better way?
Is there a library with a utility such as this?
Is implementing this an overkill?
And the best of all, can this be done in Java without the object overhead like in C++?

A: 

When using an object relational mapper such as hibernate, objects not (yet) loaded from the database are usually represented with lazy loading proxies. These implement the interface of the actual entity, and transparently load the entity from the database when any method is invoked on it.

For instance, one could use these as follows:

Person person = session.get(Person.class, someId);
Person spouse = person.getSpouse(); // proxy, unless configured otherwise

Task t = new TalkToSpouseTask(spouse);
session.save(t);

With this code, the spouse is not loaded from the database.

And the best of all, can this be done in Java without the object overhead like in C++?

Nope, you always pay the object tax in Java (unless you use primitive types, which isn't type safe). However, I have yet to see a business application where that overhead matters.

meriton
A: 

Java is pretty restrictive when it comes to respecting OO, and I don't think there's any equivalent to typedef, which C++ seems to have inherited from C. The only way I can think of is using wrappers.

As for:

void foo(Identifier<Book> book);
void foo(Identifier<Person> person);

it won't work because generics are only used at the compiler level. They get erased after the compilation step, so both those functions would become:

void foo(Identifier param);

where Identifier is the raw type (without generics), and they would be indistinguishable.

Andrei Fierbinteanu
But at compile time it would be distinguishable, which is what the OP is looking for.
GreenieMeanie
That doesn't matter; if the compiler would allow both to exist in the same class, you could then call foo(someIdentifier), and when it would come time to run the program, it wouldn't be clear which of the foos to call, since they have the same signature (but most likely different code).
Andrei Fierbinteanu
I was hoping there is some way of getting similar results to the syntax above. I see that Spring and hibernate are doing wonders with annotations, and there is the aspectJ wizardy, maybe the solution lies there?
shoren
A: 

The short answer is no, if you want type safety, you need the Object overhead (beyond varying your primitives if possible (e.g. one is a float and the other an int)).

When you program in Java you don't worry about Object overhead until it matters - that is when you see a problem profiling the implementation. Most of the time the JIT gets rid of problems, or it just isn't a problem to begin with.

Occasionally it does matter, but don't try too much prematurely guess where - you will most likely be wrong.

Yishai
I don't mind paying the Object overhead, but I was hoping not to implement an ID class for every entity.
shoren
@shoren, if you use Generics, you don't need to. You can have one class and just have different a Generic parameter in each instance according to the entity.
Yishai
+2  A: 

a more Java-equese and more correct Object Oriented version would be.

personDao.findByBookOwner(book);

inside each method they would extract the the id they need. This is the most Object Oriented way of creating an API.

fuzzy lollipop
I would have suggested this solution. I'm not sure how that would work out with a framework such as Hibernate though.
James P.
The implementation of findByBookOwner can still make the mistake of using book.getId(), and as a side note, I'm not sure I want personDao to know about the existence of books.
shoren
passing id's around in a Object Oriented system is a code smell. If you write the code to use the wrong id that isn't a compiler problem that is a PEBKAK. Compiler can't do anything about that.
fuzzy lollipop
A: 

I see that there is no good way for doing what I wanted. Even if I use class hierarchy I have to manually integrate it with automatic id generators (like JPA annotation). Too much work. Will just have to be carefull about that. A possible solution will be Annotation + inspection, something like what intellij does with @Nullable and @NotNull, but I will not implement something like this myself.

shoren