Daddy Warbox
Daddy Warbox

Reputation: 4598

Java: Trouble with Generics & Collection type detection

I have a class called DataSet with various constructors, each specifying a different type of variable. It might look a bit like this:

public class DataSet
{
    private HashSet Data;


    public DataSet( DataObject obj )
    {
        Data = new <DataObject>HashSet();
        Data.add( obj );
    }

    public DataSet( ObjectRelationship rel )
    {
        Data = new <ObjectRelationship>HashSet();
        Data.add( rel );
    }
    // etc.

Note: I haven't yet gotten to test that code due to incomplete parts (which I'm building right now).

In a function that I'm currently building, getDataObjects(), I need to return all DataObject objects that this set represents. In the case of constructors that initiate the class's HashSet, Data with types other than DataObject (such as the above ObjectRelationship), there obviously won't be any DataObjects stored within. In this case, I need to be able to detect the type that the HashSet 'Data' was initiated with (like, to tell if it's 'ObjectRelationship' or not, I mean). How do I do this?



tl;dr: How do I tell the type that a Collection (in this case, a HashSet) was initiated with in my code (like with an 'if' or 'switch' statement or something)?

Upvotes: 0

Views: 1184

Answers (8)

davetron5000
davetron5000

Reputation: 24861

Sounds like your design needs to be re-thought.

Also, to be clear on Generics; you cannot access the type at runtime. The type parameter is only for compile-time checking and is completely gone (type erasure) at runtime.

Upvotes: 1

duffymo
duffymo

Reputation: 308938

I don't know what DataObject gives you over and above an Object.

I think an object-oriented approach to your problem would use classes that reflected your domain of interest (e.g., Invoice, Customer, etc.). The persistence layer would hide the persistence details.

A common way to accomplish this is to use the Data Access Object, which might look like this in Java:

public interface GenericDao<T>
{
    T find(Serializable id);
    List<T> find();
    void save(T obj);
    void update(T obj);
    void delete(T obj);
}

Now you're dealing with objects instead of things that smack of relational databases. All the CRUD details are hidden behind the DAO interface.

Upvotes: 0

Milhous
Milhous

Reputation: 14653

You could use a map to the set

HashMap <Class<?>, HashSet<Object>> data;
HashSet temp = data.get(DataObject.class);
if(temp == null)
{
   temp = new HashSet();
   data.put(DataObject.class, temp);
}
temp.add(obj);

Then you will get the best of both worlds.

Upvotes: 1

Daddy Warbox
Daddy Warbox

Reputation: 4598

I'm going to follow duffymo's advice and just use better abstraction. I'm going to make multiple classes for each specific type I plan to use (each implementing a common interface) so that I can just bypass this dumb problem.

It'll add a minuscule bit of overhead during the process of creating each DataSet object of correct type, but I suppose that's just how it goes.

Upvotes: 0

Joel Coehoorn
Joel Coehoorn

Reputation: 416039

Sounds like you want to make the entire class generic- add a template parameter to the declaration for the class and define your HashSet and retrieval functions using that template parameter for the types.

I'm a .Net guy at the moment, though, so I couldn't give you the Java syntax, but using C# syntax it would look something like this:

public class DataSet<T>
{
    private Set<T> Data;

    public DataSet( T obj )
    {
        Data = new HashSet<T>();
        Data.add( obj );
    }

    public Iterator<T> getDataObjects()
    {
        return Data.iterator;
    }
}

Upvotes: 4

Jason
Jason

Reputation: 15931

You could add an property to your dataset class (an enumerated value, boolean or type) that specifies which type was used to initialize the hashset.

Set the property in the appropriate constructor. This allows you to bypass getting an element out of the collection to check its type.

pseudo-code:

public class DataSet
{
private HashSet Data;
private Type _iw = null;
public Type InitializedWith { return _iw; }

public DataSet(DataObject)
{
...
_iw = typeof(DataObject);
}

public DataSet(ObjectRelationship)
{
...
_iw = typeof(ObjectRelationship)
}

Upvotes: 0

duffymo
duffymo

Reputation: 308938

What does this class offer that CachedRowSet does not?

Sorry, I don't consider this to be a very good abstraction. If I were a member of your team, I wouldn't use it.

Your syntax doesn't look correct to me, either. IntelliJ agrees with me: it won't compile.

This does:

import java.util.HashSet;
import java.util.Set;
import java.util.Arrays;

public class DataSet
{
    private Set<DataObject> data;


    public DataSet(DataObject obj)
    {
        this.data = new HashSet<DataObject>();
        data.add(obj);
    }

    public DataSet(DataObject[] objs)
    {
        data = new HashSet<DataObject>();
        data.addAll(Arrays.asList(objs));
    }
    // etc.
}

Still a poor abstraction. Rethink it.

Upvotes: 0

Loki
Loki

Reputation: 30940

You could fetch an object from the set and verify its type.

Or you could have multiple sets to contain different types.

Or you could have an instance variable of type Class to act as a discriminator as an instance variable.

Or you could create a proxy object for HashSet using the last technique.

Upvotes: 1

Related Questions