generics in constructors in Java? - java

Consider this hypothetical class (which I found in a online video):
public class Contrived<T extends Number> extends ArrayList<T> {
List <? extends T> values;
......
}
Here the type variables that Contrived can accept is Number or some sub-type of Number. But the class itself inherits from ArrayList, so what-ever type the class gets, will determine the type of ArrayList.
Now there is a field called values, which is List<? extends T values>. so what does this mean here?
Does the list can hold anything that extends T (which in turn extend Number).
by PECS (producer extends, consumer super) rule, can I only use this List to read elements but not add to the list.
how will the constructor look like, if I need to pass a list of doubles or some type T?

You can have a constructor that takes a List<T> or a List<? extends T>. Continuing to contrive classes:
class B extends Number{
public double doubleValue() { return 0; }
public float floatValue() { return 0; }
public long longValue() { return 0; }
public int intValue() { return 0; }
}
class C extends B{}
I add a contrived, legal constructor to the Contrived class, taking a List<? extends T>:
public Contrived(List<? extends T> values)
{
this.values = values;
}
This allows me to write a main method such as the following:
public static void main(String[] args)
{
List<C> bList = Arrays.asList(new C(), new C(), new C());
Contrived<B> ci = new Contrived<B>(bList);
}
I can pass a List<C> into the constructor for a Contrived<B>.
Another design for the contrived constructor that is legal for Contrived could be, taking a List<T>:
public Contrived(List<T> values)
{
this.values = values;
}
Such a constructor doesn't allow a List<C> for a Contrived<B>, because now the types must match, as in the following two Contrived examples:
public static void main(String[] args)
{
List<C> cList = Arrays.asList(new C(), new C(), new C());
Contrived<C> ci = new Contrived<C>(cList); // must match now
}
And
public static void main(String[] args)
{
List<B> bList = Arrays.asList(new B(), new B(), new B());
Contrived<B> ci = new Contrived<B>(bList); // must match now
}

Related

How to subclass generics properly?

I have searched other questions/answers, an I'm still confused about this JAVA ISSUE.
If I want to subclass a type that takes generic parameters, how would I do so while using generic parameters for the subclass too without it getting shadowed?
For example:
public class A <T> extends ArrayList<T> {}
So if i instantiate my custom class, with say, Integers as the parameter, does it take that value as parameter for <T> for the ArrayList part of it too? If not, then how would I specify the type for the ArrayList?
And I know sub classing containers may not be the best idea in many situations, but in this case I have decided it would be appropriate.
yes that would be how one goes about it.
public static void main(String[] args) {
B<Integer> b = new B<Integer>();
b.a = 1;
b.b = "one";
b.add(1);
b.add(2);
}
public static class A<T> extends ArrayList<T> {
public T a;
}
public static class B<T> extends A<T> {
public T b;
}
if you like you could even have them have different types, as long as you supply the super-class with it's type as well:
public static void main(String[] args) {
B<Integer, String> b = new B<Integer, String>();
b.a = 1;
b.b = "one";
b.add(1);
b.add(2);
}
public static class A<T> extends ArrayList<T> {
public T a;
}
public static class B<U, T> extends A<U> {
public T b;
}

Generics and static methods

I have this static method
public static List<? extends A> myMethod(List<? extends A> a) {
// …
}
which I'm calling using
List<A> oldAList;
List<A> newAList = (List<A>) MyClass.myMethod(oldAList);
This gives a warning because of the unchecked cast to List<A>. Is there any way of avoiding the cast?
You need to define the type returned matches the argument (and extends A)
public static <T extends A> List<T> myMethod(List<T> a) {
// …
}
Then you can write
List<E> list1 = .... some list ....
List<E> list2 = myMethod(list1); // assuming you have an import static or it's in the same class.
or
List<E> list2 = SomeClass.myMethod(list1);
You are casting it to the parent A, if you want to avoid that then change your return type for myMethod:
public static List<T> myMethod(List<T> a) {
// …
}
if you define:
public static <T extends A> List<T> myMethod(List<T> a) {
// …
}
then you can call:
List = MyClass.myMethod(List a){}
it is generic method, is`nt it?
Jirka
This is how you can avoid the cast with static methods:
public class MyClass {
public static List<? extends A> myMethod(List<? extends A> a) {
return a;
}
public static void main(String[] args) {
List newList = new ArrayList<A>();
List<?> newList2 = new ArrayList<A>();
List<B> oldList = new ArrayList<B>();
newList = MyClass.myMethod(oldList);
newList2 = MyClass.myMethod(oldList);
}
}
In the code above, B extends A. When newList variable is defined as List without generics or as List with wildcard type (List< ? >) cast is not necessary. On the other hand if you only want to get rid the warning you can use '#SuppressWarning' annotation. Check this link for more info What is SuppressWarnings ("unchecked") in Java?
Here is simple example for #SuppressWarnings ("unchecked"):
public static List<? extends A> myMethod(List<? extends A> a) {
// …
}
#SuppressWarnings ("unchecked")
newAList = (List<A>) MyClass.myMethod(oldAList);

How can I have instances of Base and Sub-Classes in one List?

I'm currently facing an issue with base and subclasses.
While having a single object as parameter (method single) the compiler doesn't complain.
But if it comes to lists the compiler forces me to declare the list as <? extends Base>
After that I'm no longer allowed to add objects of the base type to that list.
How can I use both types (Base and Subclass) in one list?
public class Generics {
class Base { }
class Sub extends Base{ }
interface I {
public void list( List<Sub> list );
public void single( Sub p);
}
class C implements I {
public void list( List<Sub> list) { }
public void single( Sub p) { }
}
void test() {
C c = new C();
c.single( new Sub() );
c.list( new ArrayList<Base>() ); // The method list(List<Generics.Sub>) in the type Generics.C is not applicable for the arguments (ArrayList<Generics.Base>)
}
public static void main( String[] args) {
Generics g = new Generics();
g.test();
}
}
Change:
public void list(List<Sub> list);
to:
public void list(List<? extends Base> list);
Using just List<Base> will give you compiler errors like this one:
public static void main(String[] args) {
List<Sub> subs = new ArrayList<Sub>();
doSomethingWith(subs); // The method doSomethingWith(List<Base>) in the type Main is not applicable for the arguments (List<Sub>)
}
private static void doSomethingWith(List<Base> bases) {
// Do something with bases
}
If all you're going to pass is List<Base> to doSomethingWith, then this point is moot, since this won't give you a compiler error. If you want to pass lists that are of a specific type (such as List<Sub> above), then you need to change doSomethingWith to:
private static void doSomethingWith(List<? extends Base> bases) {
This fixes the problem. You could also do it at the caller lever (but it's a bit messier):
List<Sub> subs = new ArrayList<Sub>();
doSomethingWith(new ArrayList<Base>(subs));
One issue with the wildcard (?) approach is that you can't add new items to the list. To do that, you need something like:
private static <B extends Base> void doSomethingWith(List<B> bases) {
And then add only B instances to bases.
Just declare all your lists as
List<Base> list;
Then you can add both Base objects and objects of any subclass.
Below are the 2 ways to do it....
public void inTake(List<? extends Base> list){
}
Or
public T extends Base void inTake(List<T> list){
}

inheritance & collections related compile error

I'm trying to figure out why this code won't compile.
I have interface A extended by interface B.
Class C which implements interface B.
When I call a method that takes in a single object of type A, I can pass in an object of type C and it's fine.
When I call a method that takes in a java.util.List of type A, I cannot pass in a java.util.List of objects of type C. Eclipse generates the following error:
The method addAList(List) in the type Test1 is not applicable for the arguments (List)
Source code example is below.
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public void addASingle(A a) {
return;
}
public void addAList(List<A> aList) {
return;
}
// **********************************
public static void main(String[] args) {
Test1 t = new Test1();
C c1 = new C();
List<C> cList = new ArrayList<C>();
cList.add(c1);
t.addASingle(c1); // allowed
t.addAList(cList); // The method addAList(List<Test1.A>)
// in the type Test1 is not applicable for the arguments (List<Test1.C>)
}
// **********************************
public static interface A {
}
public static interface B extends A {
}
public static class C implements B {
}
}
A List<Car> is not a List<Vehicle>. If it was, you could do the following:
List<Car> cars = new ArrayList<>();
List<Vehicle> vehicles = cars;
vehicles.add(new Bicycle());
and you would end up with a list of cars which contains a bicycle. It would ruin the type-safety of generic collections.
You probably should used a List<? extends A> instead of List<A>. List<? extends A> means: a List<some class which is A or which extends A>.
It expects List and you are passing List,
Change it to
public void addAList(List<? extends A> aList) {
return;
}
it expects List of type A....write it in your method signature.
public void addAList(List<? extends A> aList) {
return;
}
by writing this you declare that..your method expects any List which contains any subtype of A...This is called wildcard.

Java generics: How to encode a Functor interface in Java?

I want to define a Functor class in Java. This works:
//a Function
public interface F<A,R> {
public R apply(A a);
}
public interface Functor<A> {
public <B> Functor<B> fmap(F<A,B> f);
}
However the return value of fmap should be not Functor, but the appropriate subclass. Usually this can be encoded with the CRTP, but here I seem to hit a wall because of the additional parameter A. E.g. the following and similar encodings don't work ("type parameter FInst is not within its bounds"):
public interface Functor<A, FInst extends Functor<A,FInst>> {
public <B, I extends Functor<B,FInst>> I fmap(F<A,B> f);
}
[Clarification]
With "appropriate subclass" I mean the type of the class being called itself. E.g. Lists are functors, so I would like to write something like
public class ListFunctor<A> implements ??? {
final private List<A> list;
public ListFunctor(List<A> list) {
this.list = list;
}
#Override
<B> ListFunctor<B> fmap(F<A,B> f) {
List<B> result = new ArrayList<B>();
for(A a: list) result.add(f.apply(a));
return new ListFunctor<B>(result);
}
}
I'm aware that I could write this even with the first definition I gave (because covariant return types are allowed), but I want that the return type "ListFunctor" is enforced by the type system (so that I can't return a FooFunctor instead), which means that the Functor interface needs to return the "self-type" (at least it is called so in other languages).
[Result]
So it seems what I want is impossible. Here is a related blog-post: http://blog.tmorris.net/higher-order-polymorphism-for-pseudo-java/
[Aftermath]
I stumbled over this age-old question of mine, and realized that this was the starting point of the amazing journey with my library highJ, containing much more than a simple Functor. I would have never imagine that people would use this crazy stuff for anything serious, but it happened, and that makes me very happy.
public interface Functor<A, FInst extends Functor<A,FInst>> {
public <B, I extends Functor<B,FInst>> I fmap(F<A,B> f);
}
This code generates an error because when you define I, you define it to be a subclass of Functor<B,FInst>, but the FInst parameter must be a subclass of Functor<B,FInst> in this case, while it is defined above as being a subclass of Functor<A,FInst>. Since Functor<A,FInst> and Functor<B,FInst> aren't compatible, you get this error.
I haven't been able to solve this completely, but I could do at least a half of the job:
import java.util.ArrayList;
import java.util.List;
interface F<A,R> {
public R apply(A a);
}
interface Functor<A, FClass extends Functor<?, FClass>> {
public <B> FClass fmap(F<A,B> f);
}
public class ListFunctor<A> implements Functor<A, ListFunctor<?>> {
final private List<A> list;
public ListFunctor(List<A> list) {
this.list = list;
}
#Override
public <B> ListFunctor<B> fmap(F<A,B> f) {
List<B> result = new ArrayList<B>();
for(A a: list) result.add(f.apply(a));
return new ListFunctor<B>(result);
}
}
This works, and it properly limits the set of allowed return types to ListFunctor, but it doesn't limit it to subclasses of ListFunctor<B> only. You could declare it as returning ListFunctor<A> or any other ListFunctor, and it would still compile. But you can't declare it as returning a FooFunctor or any other Functor.
The main problem with solving the rest of the problem is that you can't limit FClass to subclasses of ListFunctor<B> only, as the B parameter is declared at the method level, not at the class level, so you can't write
public class ListFunctor<A> implements Functor<A, ListFunctor<B>> {
because B doesn't mean anything at that point. I couldn't get it working with the second parameter to the fmap() either, but even if I could, it would just force you to specify the return type twice - once in the type parameter and once more as the return type itself.
Looking from a different angle, it seems Functor shouldn't be modeled as a "Wrapper" around the data, but actually more like a type-class, which works on the data. This shift of perspective allows to encode everything without a single cast, and absolutely type-safe (but still with a lot of boilerplate):
public interface Functor<A, B, FromInstance, ToInstance> {
public ToInstance fmap(FromInstance instance, F<A,B> f);
}
public class ListFunctor<A,B> implements Functor<A, B, List<A>, List<B>> {
#Override
public List<B> fmap(List<A> instance, F<A, B> f) {
List<B> result = new ArrayList<B>();
for(A a: instance) result.add(f.apply(a));
return result;
}
}
List<String> stringList = Arrays.asList("one","two","three");
ListFunctor<String,Integer> functor = new ListFunctor<String,Integer>();
List<Integer> intList = functor.fmap(stringList, stringLengthF);
System.out.println(intList);
//--> [3, 3, 5]
It seems I was too focused on packing both FromInstance and ToInstance in one type parameter (e.g. List in ListFunctor), which isn't strictly necessary. However, it's a heavy burden to have now not only A but also B as type parameter, which may make this approach practically unusable.
[Research]
I found a way to make this version at least a little bit useful: This functor can be used to lift a function. E.g. if you have F<String, Integer>, you can construct a F<Foo<String>, Foo<Integer>> from it when you have a FooFunctor defined as shown above:
public interface F<A,B> {
public B apply(A a);
public <FromInstance, ToInstance> F<FromInstance, ToInstance> lift(
Functor<A,B,FromInstance, ToInstance> functor);
}
public abstract class AbstractF<A,B> implements F<A,B> {
#Override
public abstract B apply(A a);
#Override
public <FromInstance, ToInstance> F<FromInstance, ToInstance> lift(
final Functor<A, B, FromInstance, ToInstance> functor) {
return new AbstractF<FromInstance, ToInstance>() {
#Override
public ToInstance apply(FromInstance fromInstance) {
return functor.fmap(fromInstance, AbstractF.this);
}
};
}
}
public interface Functor<A, B, FromInstance, ToInstance> {
public ToInstance fmap(FromInstance instance, F<A,B> f);
}
public class ListFunctor<A, B> implements Functor<A, B, List<A>, List<B>> {
#Override
public List<B> fmap(List<A> instance, F<A, B> f) {
List<B> result = new ArrayList<B>();
for (A a : instance) {
result.add(f.apply(a));
}
return result;
}
}
//Usage:
F<String, Integer> strLenF = new AbstractF<String, Integer>() {
public Integer apply(String a) {
return a.length();
}
};
//Whoa, magick!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
F<List<String>,List<Integer>> liftedF = strLenF.lift(new ListFunctor<String, Integer>());
List<String> stringList = Arrays.asList("one", "two", "three");
List<Integer> intList = liftedF.apply(stringList);
System.out.println(intList);
//--> [3, 3, 5]
I think it's still not very useful, but at least way cooler than the other attempts :-P
Building on the answer of Sergey, I think I came close to what I wanted. Seems like I can combine his idea with my failed attempt:
public interface Functor<A, Instance extends Functor<?, Instance>> {
public <B, I extends Functor<B,Instance>> I fmap(F<A,B> f);
}
public class ListFunctor<A> implements Functor<A, ListFunctor<?>> {
final private List<A> list;
public ListFunctor(List<A> list) {
this.list = list;
}
#Override
public <B, I extends Functor<B, ListFunctor<?>>> I fmap(F<A,B> f) {
List<B> result = new ArrayList<B>();
for(A a: list) result.add(f.apply(a));
return (I) new ListFunctor<B>(result);
}
}
List<String> list = java.util.Arrays.asList("one","two","three");
ListFunctor<String> fs = new ListFunctor<String>(list);
ListFunctor<Integer> fi = fs.<Integer,ListFunctor<Integer>>fmap(stringLengthF);
//--> [3,3,5]
The remaining problem is that I could write e.g. ListFunctor<StringBuilder> fi = fs.<Integer,ListFunctor<StringBuilder>> without complaints from the compiler. At least I can look for a way to hide the ugly guts behind a static method, and to enforce that relation behind the scenes...
Does anyone still use Java and ponder this problem? You might find this useful...
I've been pondering this for a looooong time. I believe I've made something satisfactory. What I would really like to is indeeed impossible in Java.
This is ideal:
interface Functor<T, CONCRETE<A> extends Functor<A, CONCRETE>> {
CONCRETE<U> fmap(Func<T, U>);
}
Unfortunately, this is make-believe syntax. This kind of thing is possible in C++ with template-template parameters, but not Java.
I was tempted to write this simple thing:
interface Functor<T> {
Functor<U> fmap(Func<T, U>);
}
This works in some cases, because an implementation can return a covariant return type (for example, List could return a List from this function), but it breaks down when you try passing around generic variables of type "F extends Functor", or a subclass of Functor, etc...
What I ended up doing was introduce a "dummy type variable", like so:
interface Functor<CONCRETE, T> {
Functor<CONCRETE, U> fmap(Func<T, U>);
}
The "concrete type" should be the type itself, or some dummy type that guarantees the uniqueness of its implementors. Here's an example implementation:
public final class Array<T> implements Functor<Array<?>, T> {
private final T[] _values;
#SafeVarargs
public Array(T... values) {
_values = values;
}
#SuppressWarnings("unchecked")
#Override
public <A, RESULT extends Functor<Array<?>, A>> RESULT fmap(Function<T, A> f) {
A[] result = (A[]) new Object[_values.length];
for (int i = 0; i < _values.length; ++i) {
result[i] = f.apply(_values[i]);
}
return (RESULT) new Array<A>(result);
}
}
The cast to (RESULT) is safe because there can only be one type that matches "Functor, T>", and that's "Array". The disadvantage of this, is that generic code may need to pass around this "CONCRETE" type in a bunch of places, and it makes your signatures unwieldy. For instance:
public class Test {
public static <CONCRETE, FInt extends Functor<CONCRETE, Integer>, FBool extends Functor<CONCRETE, Boolean>> FBool intToBool(FInt ints) {
return ints.fmap(x -> x > 5);
}
public static void main() {
Array<Integer> ints = new Array<>();
Array<Boolean> bools1 = ints.fmap(x -> x > 5); // this works because Array<> implements fmap covariantly
Array<Boolean> bools2 = intToBool(ints); // but this also works thanks to our dummy CONCRETE type
}
}
I think you want to do something that makes no sense (type wise).
interface Getter<Type> {
Type get();
}
If your application wants a getter that returns Integers, don't give it one that returns Objects.
If you don't know if it will return Objects or Integers you are trying to do something the wrong way.
If YOU KNOW it will return Integers, then wrap the getter so that it casts to integers.
Hope this is what you are looking for .
EDIT:
Explanation of why (I think) this can not be done.
Objects have there types set when you use new.
Take each type and replace it with a letter.
Take any number of another objects and do the same.
What letter do you want your function to return?
If the answer is that you want a mix, well then its too late. Types are decided at new, and you are already past new.

Categories

Resources