tags:

views:

184

answers:

3

We use make heavy usage of java.util.UUID in our projects to identify ojbects and do operations on them like in this interface:

List<UUID> searchPerson(String text);
Person fetchPerson(UUID personUUID);

List<UUID> searchAdress(String text);
Person fetchAdress(UUID adressUUID);

But what can happen now, and is a source of Runtime errors, is that a developer accidently passes a personUUID to the fetchAdress method, which should not happen.

Is there any way to make this kind of "type safe" so hat he can't pass the fetchAdress method a personUUID? Maybe there is a way to do this using generics?

+9  A: 

Build a class that includes UUID functionality by composition, and then subclass it for each individual "type" of UUID you need.

If you don't need/want the full UUID API on your subclasses, you could be extra lazy and just wrap it. Something like this:

public class MyUUID {
    private UUID uuid;
    public MyUUID() {
         uuid = UUID.randomUUID();
    }

    public UUID getUUID() {
        return uuid;
    }
}

public class PersonUUID extends MyUUID { }
public class AddressUUID extends MyUUID { }

If manually unwrapping to get the UUID object out annoys you, just implement the full UUID API on MyUUID and delegate each call to the uuid member.

Cory Petosky
+2  A: 

As far as I know, there is no real way to use a 'type safe' UUID. One thing to try would be to create an empty person object and set its UUID value. So your signature could be something like:

Person fetchAdress(Person personWithOnlyUUIDSet);

The fetch mechanism would be responsible for populating the Person object sent in with the correct values from the data store.

bogertron
I second this option. +1
Adeel Ansari
+5  A: 

Well since all you need is a typed UUID you can simple create an interface for that using generics.

package com.stackoverflow.q1747780;

public interface UUIDTyped<T>
{
    public UUID value();
}

Now assuming that Person and Address UUIDs are created from different sources you can have a class for each, implementing that interface.

package com.stackoverflow.q1747780;

import java.util.UUID;

public class UUIDFactory
{
    public static class PersonUUID implements UUIDTyped<Person>{

        /* (non-Javadoc)
         * @see com.stackoverflow.q1747780.UUIDTyped#value()
         */
        @Override
        public UUID value() {
        return UUID.randomUUID();
        }};


    public static class AddressUUID implements UUIDTyped<Address>{


        /* (non-Javadoc)
         * @see com.stackoverflow.q1747780.UUIDTyped#value()
         */
        @Override
        public UUID value() {
        return UUID.randomUUID();
        }};


    public <T> UUIDTyped<T> newUUID() {
    return new UUIDTyped<T>()
    {        
        /**
         * There is no difference on how Person and Address UUIDs are generated
         */
        @Override
        public UUID value() {
        return UUID.randomUUID();
        }
    };        
    }

    public UUIDTyped<Person> newPersonUUID(){
    return new PersonUUID();
    }

    public UUIDTyped<Address> newAddressUUID(){
    return new AddressUUID();
    }
}

Proof of concept

package com.stackoverflow.q1747780;

import junit.framework.Assert;

import org.junit.Test;


public class UUIDFactoryTest
{
    @Test
    public void testPersonUUID()
    {
        UUIDFactory uuidFactory = new UUIDFactory();

        UUIDTyped<Person> newUUID = uuidFactory.newPersonUUID();

        Assert.assertNotNull(newUUID.value());
    }

    @Test
    public void testAddressUUID()
    {
        UUIDFactory uuidFactory = new UUIDFactory();

        UUIDTyped<Address> newUUID = uuidFactory.newAddressUUID();

        Assert.assertNotNull(newUUID.value());
    }
}

Else you can get away with just dummy interfaces.

Proof of concept

package com.stackoverflow.q1747780;

import junit.framework.Assert;

import org.junit.Test;

public class UUIDFactoryTest
{
    @Test
    public void testNewUUID()
    {
        UUIDFactory uuidFactory = new UUIDFactory();

        UUIDTyped<Person> newUUID = uuidFactory.newUUID();        
        UUIDTyped<Address> addressUUID = uuidFactory.newUUID();

        Assert.assertNotNull(newUUID.value());
        Assert.assertNotNull(addressUUID.value());
    }    
}

Finally your service will be like

package com.stackoverflow.q1747780;

import java.util.List;

public interface Service<T>
{
    public List< UUIDTyped<T> > search(String text);

    public T fetch( UUIDTyped<T> uuid);
}

with a PersonService class

package com.stackoverflow.q1747780;

import java.util.List;

public class PersonService implements Service<Person>
{

    /* (non-Javadoc)
     * @see com.stackoverflow.q1747780.Service#fetch(com.stackoverflow.q1747780.UUIDTyped)
     */
    @Override
    public Person fetch(UUIDTyped<Person> uuid) {
    return null;
    }

    /* (non-Javadoc)
     * @see com.stackoverflow.q1747780.Service#search(java.lang.String)
     */
    @Override
    public List< UUIDTyped<Person> > search(String text) {
    return null;
    }
}

and an AddressService class

package com.stackoverflow.q1747780;

import java.util.List;


public class AddressService implements Service<Address>
{

    /* (non-Javadoc)
     * @see com.stackoverflow.q1747780.Service#fetch(com.stackoverflow.q1747780.UUIDTyped)
     */
    @Override
    public Address fetch(UUIDTyped<Address> uuid) {
        // TODO Auto-generated method stub
        return null;
    }

    /* (non-Javadoc)
     * @see com.stackoverflow.q1747780.Service#search(java.lang.String)
     */
    @Override
    public List<UUIDTyped<Address>> search(String text) {
        // TODO Auto-generated method stub
        return null;
    }

}

Testing correct type for PersonService

package com.stackoverflow.q1747780;

import java.util.List;

import org.junit.Test;



public class PersonServiceTest
{
    @Test
    public void testFetch()
    {
        UUIDFactory uuidFactory = new UUIDFactory();
        PersonService service = new PersonService();

        Person person = service.fetch( uuidFactory.newPersonUUID() );        
    }

    @Test
    public void testSearch()
    {
        PersonService service = new PersonService();

        List< UUIDTyped<Person> > uuidList = service.search("foo");        
    }
}

Testing correct type for AddressService

package com.stackoverflow.q1747780;

import java.util.List;

import org.junit.Test;

public class AddressServiceTest
{
    @Test
    public void testFetch()
    {
        UUIDFactory uuidFactory = new UUIDFactory();
        AddressService service = new AddressService();

        Address address = service.fetch( uuidFactory.newAddressUUID() );        
    }

    @Test
    public void testSearch()
    {
        AddressService service = new AddressService();

        List< UUIDTyped<Address> > uuidList = service.search("foo");        
    }
}
Cue