Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restrict types in Java for use in a List

Tags:

java

generics

How can I pass a List<T> with a set of certain allowed types I did not declare to a method.

E.g. Restrict types to Integer, Boolean and String:

// Pseudo code
public void method(List<Integer OR Boolean OR String> myList);

If I use List<Object> I can put everything into that list:

public void method(List<Object> myList);

If I use List I can put all instances of Parent and its subclasses into that list:

public void method(List<Parent> myList);

That would be enough if I am the one who declares those subclasses (AllowedTypeA extends Parent). But what can I do when I'm not the owner of the classes I want to use (I cannot make Integer extend Parent)?

like image 434
Sebastian Barth Avatar asked Dec 31 '14 15:12

Sebastian Barth


People also ask

Can a Java list contains different types?

You can add any Java object to a List . If the List is not typed, using Java Generics, then you can even mix objects of different types (classes) in the same List . Mixing objects of different types in the same List is not often done in practice, however.

How do I restrict a generic type in Java?

Java For Testers Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.

How do you restrict a class in Java?

1.3 Steps to create singleton class in Java:Create INSTANCE of same class by instantiating class & this INSTANCE should be with private & static modifier. Provide public static method that returns same INSTANCE of class every time. Finally, create private constructor so that no-one create object from outside of class.


Video Answer


2 Answers

The best thing to do is to wrap this mixed list into a class and provide methods to add just what you want to permit:

class WrappedMix {
    private List<Object> oddsAndEnds = ...

    public void add( Integer el ){ oddsAndEnds.add( el ); }
    public void add( Boolean el ){ oddsAndEnds.add( el ); }
    public void add( String el ){ oddsAndEnds.add( el ); }
}

Or extend ArrayList with suitable overriding (and overloading),

Although I'm curious why you'd want such a List - it's processing isn't convenient.

like image 61
laune Avatar answered Sep 22 '22 21:09

laune


Conceptually, I would prefer @laune's solution much more. I'd prefer type safety and compile errors over willy-nilly throwing a bunch of things into a list and forgetting to add the permitted Type.

That being said, it is still possible to do, though you'll have to do a bunch of extra stuff to make this practical, i.e. if you remove the object type, you should also probably remove all objects that are associated with it, and the other methods need to be overridden such as addAll to ensure proper functionality.

This approach makes it more flexible compared to laune's though, because you can add permittedTypes at any time. For your situation, probably not the best, but the general question is still intriguing enough that I took a shot. Maybe you want some of your Lists to store Integers, but not others. You can do so using the addPermittedObject method.

public class SelectiveList extends ArrayList<Object> {
    //the (types of) objects that we can store
    private ArrayList<Object> permittedObjects = new ArrayList<Object>();

    // put an Object type into the list
    public boolean addPermittedObject(Object o) {
        for (Object type : permittedObjects) {
            if (type.getClass() == o.getClass()) {
                return false; // if we already have it, do not add it again
            }
        }
        return permittedObjects.add(o); // else let's add it
    }

    // remove the Object type
    public boolean removePermittedObject(Object o) {
        for (Object type : permittedObjects) {
            if (type.getClass() == o.getClass()) {
                return permittedObjects.remove(type);
            }
        }
        return false;
    }

    @Override
    public boolean add(Object o) {
        for (Object type : permittedObjects) {
            if (type.getClass() == o.getClass()) {
                return super.add(o); // go ahead and add the item since it
                                        // matches our list
            }
        }
        return false;
    }
}

And to test it:

public static void main(String[] args) {
    SelectiveList selectiveList = new SelectiveList();
    selectiveList.add("Potato");
    selectiveList.add(1);
    selectiveList.add(true);
    System.out.println(selectiveList.size()); // prints 0
    // these objects need to be initialized, but their contents do not
    // matter
    selectiveList.addPermittedObject(new String());
    selectiveList.addPermittedObject(new Boolean(false));
    selectiveList.addPermittedObject(new Integer(1));
    selectiveList.add("Potato");
    selectiveList.add(1);
    selectiveList.add(true);
    System.out.println(selectiveList.size()); // prints 3
}
like image 41
Compass Avatar answered Sep 25 '22 21:09

Compass