views:

646

answers:

8

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)?

+1  A: 

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.

Loki
A: 

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.

duffymo
So you're saying I shouldn't initialize with more than one type?
Daddy Warbox
For starters, yes. I'm also saying that this entire class feels wrong to me.
duffymo
And it simply doesn't compile. Perhaps that's just because it was an example and not intended to be representative of your executable code.
duffymo
Have a look at CachedRowSet. I think it has all the functionality that you're looking for, without the overhead of having to maintain this code.
duffymo
I can only think of using an interface and implementing it in multiple ways. I'll try that.
Daddy Warbox
CachedRowSet won't have the specific stuff that I need (that I omitted from that code sample). Nonetheless, I'll have a look at that too.
Daddy Warbox
Perhaps I'm wrong. I don't know your requirements. But I highly doubt that. Others that have gone before you have thought about persistence, and from the little I've seen you'd be better off taking advantage of it and not reinventing the wheel.
duffymo
A: 

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)
}
Jason
typeof? I'll look into that.
Daddy Warbox
i dunno if that exists in Java or not, but surely there is something similar. perhaps object.GetType() ?
Jason
IS there a typeof in Java?
Daddy Warbox
I believe its getClass() - <a href="http://www.theserverside.com/discussions/thread.tss?thread_id=38485">http://www.theserverside.com/discussions/thread.tss?thread_id=38485</a>
Jason
+4  A: 

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;
    }
}
Joel Coehoorn
But I still need to know the specific type for my code (as in, for an 'if' or 'switch' statement or something to be able to check).
Daddy Warbox
No: you're return type in that function is just "T": your template type.
Joel Coehoorn
Same for Java as this code. Very nice, but still not what I'd recommend.
duffymo
A: 

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.

Daddy Warbox
+1  A: 

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.

Milhous
Clever, but that's not exactly what I'll need. Still, I could probably use that technique for later...
Daddy Warbox
A: 

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.

duffymo
A: 

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.

davetron5000