rwallace
rwallace

Reputation: 33395

Java functional collection and unchecked warnings

I have written a purely functional collection class - a bag aka multiset - and it works fine, but generates a number of warnings for unchecked assignment, both in the class definition itself and in use, particularly in and using FBag.make. As far as I can see, the problem arises primarily from the fact that functional collections want static factory methods instead of public constructors, and static methods cannot access the generic type.

Is there an elegant way to avoid this, or do I just have to bite the bullet, accept that this is a type erasure issue, and suppress the warnings?

Code follows. (I'm aware this implementation is asymptotically inefficient. N is expected to be small; if that changes, I will change the implementation.)

import java.util.Collection;
import java.util.Iterator;

public class FBag<E> implements Iterable<E> {
    public static final FBag EMPTY = new FBag<>(null, null);
    private final FBag<E> next;
    private final E val;

    private FBag(E val, FBag<E> next) {
        this.val = val;
        this.next = next;
    }

    public FBag<E> add(E val) {
        return new FBag<>(val, this);
    }

    @Override
    public Iterator<E> iterator() {
        return new Iter<>(this);
    }

    public static FBag make(Collection c) {
        var p = EMPTY;
        for (var a : c) {
            p = p.add(a);
        }
        return p;
    }

    public static FBag make(Object[] c, int from, int to) {
        var p = EMPTY;
        for (var i = from; i < to; i++) {
            p = p.add(c[i]);
        }
        return p;
    }

    public FBag<E> remove(E val) {
        if (this == EMPTY) {
            return this;
        }
        if (this.val == val) {
            return next;
        }
        return new FBag<>(this.val, next.remove(val));
    }

    private static class Iter<E1> implements Iterator {
        private FBag<E1> p;

        Iter(FBag<E1> p) {
            this.p = p;
        }

        @Override
        public boolean hasNext() {
            return p != EMPTY;
        }

        @Override
        public Object next() {
            assert p != EMPTY;
            var r = p.val;
            p = p.next;
            return r;
        }
    }
}

Upvotes: 3

Views: 391

Answers (2)

Eran
Eran

Reputation: 393841

For the static member EMPTY, I believe there is no way around using the raw type. You can see the same in Collections.EMPTY_LIST:

@SuppressWarnings("rawtypes")
public static final List EMPTY_LIST = new EmptyList<>();

@SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}

Therefore, all you can do here is suppress the warning:

@SuppressWarnings("rawtypes")
public static final FBag EMPTY = new FBag<>(null, null);

@SuppressWarnings("unchecked")
public static final <T> FBag<T> empty() {
    return (FBag<T>) EMPTY;
}

On the other hand, for the static methods you can declare their own generic type variables.

public static <E> FBag<E> make(Collection<E> c) {
    FBag<E> p = empty ();
    for (E a : c) {
        p = p.add(a);
    }
    return p;
}

public static <E> FBag<E> make(E[] c, int from, int to) {
    FBag<E> p = empty ();
    for (int i = from; i < to; i++) {
        p = p.add(c[i]);
    }
    return p;
}

(I replaced the vars with explicit declarations, since I don't have Java 10 installed, and I didn't want to post unchecked code).

Upvotes: 2

Mureinik
Mureinik

Reputation: 311403

static method can have their own generic declarations, which you can use to create an Fbag<T> from a Collection<T> or T[]:

public static <T> FBag<T> make(Collection<? extends T> c) {
    var p = EMPTY;
    for (var a : c) {
        p = p.add(a);
    }
    return p;
}

public static <T> FBag<T> make(T[] c, int from, int to) {
    var p = EMPTY;
    for (var i = from; i < to; i++) {
        p = p.add(c[i]);
    }
    return p;
}

Upvotes: 2

Related Questions