Is this the right way to use generics in Java? - java

class Matrix<T>{
private List<Attribute<T>> attributes;
public Matrix(T type){
attributes = new ArrayList<Attribute<T>>();
attributes.add(new Attribute<T>(type));
}
}
I feel like in the constructor, these two lines should be use a specific type, not the generic T:
attributes = new ArrayList<Attribute<T>>();
attributes.add(new Attribute<T>(type));
But the compiler doesn't complain. So this is the right way to define this class?

Yes, thats the right way. The only thing where you might be wrong is that your Parameter in the constructor should not be namend type, but value. The Type is T.
If you need to, you can say that your Generic has to a childtype of something else. Let's say we have a class which holds an Exception. We could just make a Membervariable of Type Exception. But when getting the Exception from inside this Object, we don't want to cast our Exception to a more specific one.
So we use a Generic which must be a childtype of Exception:
public class SomeClass<T extends Exception>
{
private final T value;
public SomeClass(T value)
{
this.value = value;
}
public T getValue()
{
return this.value;
}
}
Now we can do stuff like this:
SomeClass<ArrayIndexOutOfBoundsException> obj = new SomeClass<>(new ArrayIndexOutOfBoundsException());
ArrayIndexOutOfBoundsException exc = obj.getValue(); // no cast from Exception to ArrayIndexOutOfBoundsException needed

I feel like in the constructor, these two lines should be use a specific type, not the generic T
No, because your class Matrix is generic on the type parameter T. This means that it encapsulates a list of attributes of the type T, namely: List<Attribute<T>>.
If you use it with Integer:
Matrix<Integer> integerMatrix = new Matrix<>(1);
Then 1 would be inside the first attribute of the list.
However, if you declare another matrix with String:
Matrix<String> stringMatrix = new Matrix<>("hello");
Then your matrix will hold attributes that encapsulate String values.

Related

Parameterize a class with one of a fixed set of types

Say I have a generic class Foo which can hold an object of type T. Furthermore, let's say I only want to be able to instantiate the class with objects that are one of two types. Finally, let's say that the lowest common upper bound of these two types is a type that has many more subclasses than those two types that I want to allow, so I can't simply specify an upper bound for the type parameter (as in class Foo<T extends Something>), because then I would allow to instantiate the class with other types than the two I expect.
For illustration, let's say I want Foo to hold only either a String or an Integer. The lowest common upper bound is Object, so specifying an upper bound won't do the trick.
Certainly, I could do something along the lines of
class Foo<T> {
private T obj;
public Foo(T obj) throws IllegalArgumentException {
if (!(obj instanceof String || obj instanceof Integer)) {
throw new IllegalArgumentException("...");
}
this.obj = obj;
}
}
However, in this case, I can still call the constructor with any object; if I try to instantiate it with something that is neither a String nor an Integer, I will get an exception at runtime.
I would like to do better. I would like the compiler to infer statically (i.e., at compile time) that I can only instantiate this class with objects that are either String or Integer.
I was thinking something along those lines might do the trick:
class Foo<T> {
private T obj;
public Foo(String s) {
this((T) s);
}
public Foo(Integer i) {
this((T) i);
}
private Foo(T obj) {
this.obj = obj;
}
}
This works, but it looks really, really odd. The compiler warns (understandably) about unchecked casts. Of course I could suppress those warnings, but I feel this is not the way to go. In addition, it looks like the compiler can't actually infer the type T. I was surprised to find that, with the latter definition of class Foo, I could do this, for instance:
Foo<Character> foo = new Foo<>("hello");
Of course, the type parameter should be String here, not Character. But the compiler lets me get away with the above assignment.
Is there a way to achieve what I want, and if yes, how?
Side question: why does the compiler let me get away with the assignment to an object of type Foo<Character> above without even so much as a warning (when using the latter definition of class Foo)? :)
Try using static factory method to prevent compiler warning.
class Foo<T> {
private T obj;
public static Foo<String> of(String s) {
return new Foo<>(s);
}
public static Foo<Integer> of(Integer i) {
return new Foo<>(i);
}
private Foo(T obj) {
this.obj = obj;
}
}
Now you create instance using:
Foo<String> foos = Foo.of("hello");
Foo<Integer> fooi = Foo.of(42);
Foo<Character> fooc = Foo.of('a'); // Compile error
However the following are still valid since you can declare a Foo of any type T, but not instantiate it:
Foo<Character> fooc2;
Foo<Character> fooc3 = null;
Foo<Object> fooob1;
Foo<Object> fooob2 = null;
one word: interface. You want your Z to wrap either A or B. Create an interface implementing the smallest common denominator of A and B. Make your A and B implement that interface. There's no other sound way to do that, AFAIK. What you already did with your constructors etc. is the only other possibility, but it comes with the caveats you already noticed (having to use either unchecked casts, or static factory wrappers or other code smells).
note: If you can't directly modify A and/or B, create wrapper classes WA and WBfor them beforehand.
example:
interface Wrapper {
/* either a marker interface, or a real one - define common methods here */
}
class WInt implements Wrapper {
private int value;
public WInt( int value ) { this.value = value; }
}
class WString implements Wrapper {
private String value;
public WString( String value ) { this.value = value; }
}
class Foo<T> {
private Wrapper w;
public Foo(Wrapper w) { this.w = w; }
}
because you call your private Foo(T obj) due to diamond type inference. As such, it's equal to calling Foo<Character> foo = new Foo<Character>("hello");
Long story short: You are trying to create a union of two classes in java generics which is not possible but there are some workarounds.
See this post
Well the compiler uses the Character class in T parameter. Then the String constructor is used where String is casted to T (Character in this case).
Trying to use the private field obj as a Character will most likely result in an error as the saved value is an instance of the final class String.
Generics is not suitable here.
Generics are used when any class can be used as the type. If you only allow Integer and String, you should not use generics. Create two classes FooInteger and FooString instead.
The implementations should be pretty different anyway. Since Integers and Strings are very different things and you would probably handle them differently. "But I am handling them the same way!" you said. Well then what's wrong with Foo<Double> or Foo<Bar>. If you can handle Integer and String with the same implementation, you probably can handle Bar and Double and anything else the same way as well.
Regarding your second question, the compiler will see that you want to create a Foo<Character>, so it tries to find a suitable overload. And it finds the Foo(T) overload to call, so the statement is perfectly fine as far as the compiler is concerned.

Generic list conversion to an array

Assuming that I have the following class
public class A <T>{
private T [] datas;
// more code here ...
}
And I desire to take advantage of the constructor to initialize the array. Suppose that I have the following constructor
public A(T element){....}
Java does not allow me to use something like
datas = new T[10]
And it will complain that I cannot create a generic array of T
But I can still use a work around like:
#SuppressWarnings("unchecked")
public A(T element){
List<T> datasList = new ArrayList<T>();
datasList.add(element);
datas =(T[]) datasList.toArray();
}
I have a warning from the compiler that's why I had to add the #SuppressWarnings, but my point is related to the following comment from the toArray method documentation (Please take a look at the picture)
It talks about the returned array being safe. So does that means it is safe to use this method? If not why? And what would be a better way to do such an initialisation in a constructor? I would like to also consider the case of a variable list of T elements in an overloaded constructor like
public A(T... elements){....}.
You can create an instance of a generic array using the following:
public A(T element){
int length = 10;
datas = (T[])Array.newInstance(element.getClass(), length);
}
However, there's a problem if element would be a subclass of T, e.g. if you'd call it like this:
A<Number> numberA = new A<>( Integer.valueOf(1) );
Here T would be Number but the class of element would be Integer.
To mitigate that you could pass a vararg array of type T, e.g. like this:
//firstElement only exists to force the caller to provide at least one element
//if you don't want this then just use the varargs array
A(T firstElement, T... furtherElements){
int length = 10;
Class<?> elementClass = furtherElements.getClass().getComponentType();
datas = (T[])Array.newInstance( elementClass, length);
}
Since varargs always result in an array (even of length 0) you'll get an array of type T and can get the component type of that.
So in the case above numberA.datas would be a Number[] array and not an Integer[] array.
You can pass generics, but you can't call new T (or new T[ ]).
Keep in mind that generics are gone after compilation, so it actually only helps when writing the code. Knowing it's gone during runtime, it's also obvious that new T( ) can't be called as generic, T is removed in runtime.
It's safe to do, because you create that list in full control, accepting only objects of your generic type.
A nicer way (imho) is to create a static method as it is purely input-->output. You have to declare your generics before the method return type:
public < T > T[ ] toArray(T... objects) { ... }

java class declaration with the generic type disable method returning another generic type

I have this little problem and I want to find out whay is this happening to me:
I made class like this:
public class SomeSimpleClass {
//and I had method who returns generic. it can be instance of whatever, and for sure can be casted to U:
public <U>U giveMeSomething(String arg) {
return (U)SomewhereSomething.getValueCastingIsOK(arg);
}
}
and then I am using it like this:
// give me some number:
Integer number = instanceSomeSimpleClass.giveMeSomething("I want integer value");
OR
String text = instanceSomeSimpleClass.giveMeSomething("I want text now");
and everything is ok. getValueCastingIsOK from SomewhereSomething returns me what I want, and I dont need type cast on the giveMeSomething. Looks like the U is replaced by the type from the associative variable.. and everything is cool..
BUT then I create a new class:
public class SomeOtherClass <T extends Something> {
//And I have the exactly same method here:
public <U>U giveMeSomething(String arg) {
return (U)SomewhereSomething.getValueCastingIsOK(arg);
}
}
and now when I want to use it in the same way, the compiler (I am using Java 1.7) is telling me, that I have to use typecast, and start to using the giveMeSomething like this, because U is now the Object, and nothing else...
Integer number = (Integer)instanceSomeSimpleClass.giveMeSomething("I want integer value");
:(
Why in the second case the compiler cant get the U type in the same way, as in the first case. And what I have to do, to be that this way?
Thank for an answers, suggestions.. ;)
The problem is that you're using a raw type of the class SomeOtherClass, but not supplying any type parameters to your declaration:
SomeOtherClass instanceSomeSimpleClass = new SomeOtherClass();
When you do this, the Java compiler will replace ALL generics in the class with the type erasure version, even unrelated generics such as your generic method. The type erasure of the method
public <U>U giveMeSomething(String arg) {
becomes
public Object giveMeSomething(String arg) {
which requires the cast to Integer.
You must either supply a type parameter to SomeOtherClass (useless, unless you've eliminated unnecessary code that uses T to post the class code here):
SomeOtherClass<Something> instanceSomeSimpleClass = new SomeOtherClass<Something>();
Or you can remove the type parameter on the class definition, as you had it originally:
public class SomeSimpleClass {
Either will keep the generic method intact and eliminate the need to cast the return type of giveMeSomething.
You're missing the java syntax for explicit typing:
public <U>U giveMeSomething(String arg) {
return SomewhereSomething.<U>getValueCastingIsOK(arg);
}
Notice the <U> between the dot and the method name. Notice also that casting is now not required.
That's how you explicitly declare what the type is for a typed method. Without this, java must infer the type, which it can't here, so (unsafe) casting is required.
Another example to illustrate. In this case, java can infer the type is Integer because of the type that the result is being assigned to:
Integer number = instanceSomeSimpleClass.giveMeSomething("I want integer value");
But if you were to use the value without assigning it, you'd need to pass to type:
someIntegerRequired(instanceSomeSimpleClass.<Integer>giveMeSomething("I want integer value"));

Java Is there any way to initialize a generic array that's type safe?

I have the following code
public class Container<T> {
private T element;
private T[] tarray;
public T getElement() {
return element;
}
public void setElement(T element) {
this.element = element;
}
public void add(T element) {
tarray[0] = element;
}
public void CreateArray(int size) {
//Can this be cleaned up better?
tarray = (T[]) new Object[size];
}
public T get() {
return tarray[0];
}
public Container(T someElement) {
this.element = someElement;
}
public Container() {
}
public static void main(String[] args) {
Container<String> myContaier1 = new Container<String>();
myContaier1.setElement("Hello");
myContaier1.CreateArray(1);
myContaier1.add("GoodBye");
System.out.println(myContaier1.get());
}
}
Is there no way to initialize a type safe generic array?
There is no way unless you provide a reified T in the form of an actual Class<T> object that represents a specific value of T. This is because the array type is reified, whereas the Generic type isn't.
There are two problems here:
First of all, the actual type of your array will always be Object[]. You cast it to T[], but this works only because T[] erases to Object[]. If your class definition said, for example, <T extends Number>, then (T[])new Object[] would fail with a ClassCastException.
You could get around this by passing Class<T> to the constructor of your collection and keeping it in a field:
private Class<T> componentClass;
...
tarray = (T[]) Array.newInstance(componentClass, size);
Now the actual in-memory type of tarray is T[], but you still get an unchecked cast error. Even though you have the component class, as far as I know there is no equivalent of Class.cast() for doing a checked cast of an array instance.
You can do private T[] tarray;, But you cannot assign it to (T[]) new Object[size];. How can an array of Object be same an array of any other class.
T is not even there after compilation.
It is called type erasure. E.g if you do
List<Person> persons = new ArrayList<Person>();
It becomes List persons = new ArrayList() after compilation.
It is similar to ask "Is there a way to initialize a generic object: new T()?"
Of course it is impossible, as the compiler does not know what the type is of it.
Array is the same, its type relies on its elements. If the type of its elements is unknown, the type of itself is unknown.
Those classes which can take generic types like List, Set, Map, etc. are different. They have their own classes as types and they are dynamic, so you can initialize one like new ArrayList<T>().
You can try on this:
public class Test {
public static void main (String args[]) {
int[] i = new int[3];
short[] s = new short[4];
ArrayList<String> a = new ArrayList<String>();
System.out.println(i.getClass().getName());
System.out.println(s.getClass().getName());
System.out.println(args.getClass().getName());
System.out.println(a.getClass().getName());
}
}
You will see the types of elements are already combined with the arrays, while not combined with ArrayList.
There are way to do this on the JVM, but to do something like this in Java would require writing a lot of boiler plate code. In Scala, you can use Manifests to get around type erasure, and can instantiate generic arrays without casting.
An example:
class Container[T : Manifest]() {
def createArray(size:Int) = Array.ofDim[T](size)
}
scala> val f = new Container[String]
f: Container[String] = Container#12f3aa66
scala> f.createArray(5)
res7: Array[String] = Array(null, null, null, null, null)
Scala code compiles to the same bytecode class files as Java (implying that this should be possible in Java if you jumped through enough hoops and maybe wrote your own manifests). Scala classes can be imported into Java projects... though I'm not sure how hard it is to instantiate a class like this from Java.
If you often find yourself wanting to be able to handle more complicated type situations, have a look at Scala, you might like it.

I'm studying Head First Java, but I can't understand Page 544

"When you declare a type parameter for the class, you can simply use that type any place that you'd use a real class or interface type. The type declared in the method argument is essentially replaced with the type you use when you instantiate the class.
If the class itself doesn't use a type parameter, you can still specify one for a method, by declaring it in a really unusual (but available) space-before the return type, This method says that T can be "any type of Animal"."
Can you explain?
What it means is that in a generic class, you can write methods like so:
public T doSomething () {
}
Note that the return type is 'T'.
At compile-time, the return type of that method will be whatever you have passed to the generic class when you instantiated it.
class Zoo<T> {
static void putAnimal(T animal) {
// do stuff
}
}
Zoo<Ape> apeZoo = new Zoo<Ape>(); // you can now put apes (and chimps) here
Zoo<Reptile> monkeyZoo = new Zoo<Reptile>(); // this zoo takes reptiles
apeZoo.putAnimal(new Chimp());
monkeyZoo.putAnimal(new Tortoise());
For the first paragraph, this is just how generics work for classes. For instance, for list, you can create a list of a generic type, such as integer, e.g.:
ArrayList<Integer> list = new ArrayList<Integer>();
(in real code you'd use List<Integer> of course)
Now ArrayList will be defined as:
public class Arraylist<T> { // implements....
// ...
public T get(int index) {
// ...
}
}
Which is what makes it possible to use the get method on list and get an Integer (because we made a class of type ArrayList<Integer> so T = Integer). Otherwise the compiler would have no idea what types of objects the list was storing and you'd have to get the method to return an Object, which is how it used to be.
What the second paragraph means is that you can add type parameters to methods just as you can to classes. e.g.:
public <T> void noOp(T element) {
// code here
}
This would allow you, for instance, to create a static utility method that returns something of type T. To return the first element of a List of T's:
public static <T> T getFirst (List<T> list) {
return list.get(0);
}
And you could use this method in a strongly typed fashion. Suggestions for better examples welcome. :-)
edit: I just realised I once wrote something that uses this functionality. I was using the JPA API and getting really annoyed at all the times you have to return something (a list, or a single item) from a query, and running into unchecked type warnings because there's no way to infer the type here. If you're like me and trying to avoid warnings in your code, you'd have to suppress the warnings every single time. So I wrote this method to suppress the warnings for me:
#SuppressWarnings("unchecked")
public static <T> List<T> returnResultList(Query query) {
return (List<T>)query.getResultList();
}
Which through the magic of type inference works on:
List<Integer> list = returnResultList(query);

Categories