public static void main(String[] args) {
Set vals = new TreeSet();
vals.add("one");
vals.add(1);
vals.add("two");
System.out.println(vals);
}
(i)-What does it mean to define a collection without giving it a type?For what purpose
does it made for?
(ii)-Can I add different type to the collection any way?
Here's an example- There's no compilation error, though it's warning me.
But, as excepted, there's a run time error.
Defining a collection (or any typed class) without specifying a type argument is called using a raw type. It exists only for backwards compatibility and should not be used in new code. If effectively removes all effects of generics on the type.
Generally, you could define your set to accept Object: it would then accept all values: Set<Object> vals = new HashSet<Object>(). However this won't work for TreeSet, because it needs its values to be Comparable with each other. If you want to add arbitrary types to the same TreeSet (which is usually a sign of some architecture problems, i.e. a design smell), then you'll need to implement your own Comparator that can compare arbitrary elements and pass that into the appropriate TreeSet constructor.
i) It means it can have any object. If you want to add any type. (Generally a design problem)
ii) Using type erasure you can.
But, as excepted, there's a run time error.
Getting an error at compile time is usually better than getting a runtime error. (Easier to find and fix)
Upto Java 1.4 there were no Generics. All collections could only contain Object and you would have to classcast the object when using the object after taking it out of the collection. So you would do
Set vals = new Treeset();
String s = (String)vals.get(0);
instead of
Set<String> vals = new Treeset<String>();
String s = vals.get(0);
Putting different types of objects that have no shared interface or superclass is very bad practice since you wont know how to handle the object when you take it out of the collection.
private Set<JPanel> s1;
public void run() {
aMethod(s1);
}
/* line 7 */ public void aMethod(Set panels) {
}
If you refactor the above code by changing the 7th line to
public void aMethod(Set<Object> panels) {
it will no longer compile. If you refactor that line to
public void aMethod(Set<?> panels) {
it will still compile as before, and as an extra bonus you will no longer have the "Set is raw..." warning
Related
I have code like this:
public class Crate<T> {
private T contents;
public T emptyCrate() {
return contents;
}
public void packCrate(T contents)
{
this.contents = contents;
}
}
Now we know - in the end it will be "converted" to the following code:
public class Crate {
private Object contents;
public Object emptyCrate() {
return contents;
}
public void packCrate(Object contents)
{
this.contents = contents;
}
}
Then why we need to create a generics if i already can create a class like Object Based ?
When people talk about type erasure, they normally focus upon the generic class itself. But there is another important place with generics: the call site.
For example, if you've got this code:
Crate<Integer> intCrate = new Crate<>();
intCrate.packCrate(0);
Integer contents = intCrate.emptyCrate();
Then, when it is compiled, it actually becomes:
Crate intCrate = new Crate();
intCrate.packCrate(0);
Integer contents = (Integer) intCrate.emptyCrate();
// ^ Important! This cast.
i.e. there are casts inserted automatically. Also, implicitly, there is a check that the parameter of packCrate is compatible with Integer, so you couldn't write:
intCrate.packCrate("hello");
Now, you can do this without generics, putting in these casts yourself, but the compiler doesn't help you to know what was put into the Crate. You could write something like this:
Crate crate = new Crate();
crate.packCrate(0);
String contents = (String) crate.emptyCrate();
This will fail at runtime, because the crate contains an Integer, not a String.
Generics just help you not to have to remember what you are allowed to pass to an instance, and what you will get out of it.
You see, java code is translated to bytecode. So why don't you write your programs in byte code? They get translated anyway? Or, to be precise: the JIT compiler will turn most bytecode into machine code at some point. So why do you insist on writing java source code, instead of binary machine code?!
I guess the above questions make my point clear: generics allow you to express intent for human readers. They allow you to write better source code; and they enable the compiler to do certain kinds of checks on your input - as nicely summarized in the other answer by Andy Turner.
That is the whole point of any abstraction that programming languages provide to you: they help you the programmer to create source code that expresses "what needs to be done" in a concise way that makes it easier for human readers to understand what is going on, and why!
It doesn't matter what it will be turned into.
Rather the stage of compilation is important. Generics guarantee type safety at compile type (fixing compile-time errors is much easier than runtime ones).
It also eliminates casts and enables the ability to implement generic algorithms.
All collections in Java are Generics. It's the best example of using Generics. For example you create class List. Which types of objects will it hold? When you create List class, you don't know about which types it will hold, so you use Generics. And when you use Listclass, you say, that you want to put Integers(new List<Integer).
Template parameters in generics are used for compile time safety. Compiler will choke if you write for example:
Crate<Integer> cr;
Object o;
cr.packCrate(o); // compilation error here
It can also declare that any class used as parameter will implement some methods:
Interface I {
void myMethod();
}
class Crate<T extends I> {
private T contents;
...
public void applyMethod() {
contents.myMethod(); // T shall implement myMethod
}
}
In fact everything could be done by explicit casting, but you will get only run time errors while generics allow compile time detection of errors.
I have this code
public class TupleSpace implements aTupleSpace {
private Collection<aTuple> theSpace;
public TupleSpace() {
theSpace
}
public void out(aTuple v) {
theSpace.add(v);}
the .add causes an error, I think its either because .add is not defined for a generic collection type? or because im not properly understanding the line:
private Collection<aTuple> theSpace;
i have to keep this line the same but can make any changes to the out method or the rest of the class
I just want to be able to add things to a collection and search the collection for them
A Collection is just an Interface.
It defines what you can do with theSpace and is somewhat independent of what theSpace actually is. It may be a List or a Map or something entirely different.
Collection.add(E e) is indeed a method that is common to all Collections. Still the actual implementation might differ.
However, private Collection<aTuple> theSpace; is just declaring the variable.
It will be set to null when you create an instance of TupleSpace.
This is the reason for the NullPointerException that is thrown when you try to use theSpace.
Hence, you will need to create a concrete Collection instance and assign it to theSpace before you can use it (e.g. add objects).
There are plenty of Collection types that come ready to use with the SDK.
Choose one that fits your use case. Here is an example, using an ArrayList:
// ...
public TupleSpace() {
this.theSpace = new ArrayList<aTuple>();
}
// ...
Just playing around with generics and stuff in Java, came across a potential issue. Here is a simplified version of what I was thinking
public class Test<T> {
private T[] arrayOfGenerics;
int top;
public Test(int size) {
arrayOfGenerics = (T[]) new Object[size];
top = 0;
}
public void add(T toAdd) {
arrayOfGenerics[top++] = toAdd;
}
public static void main(String[] args) {
Test myTest = new Test(10);
myTest.add("Some String"); //First value added is a String
myTest.add(12); //Way to make this NOT work, since myTest should only accept Strings now
}
}
Obviously this doesn't have much use, production wise, but I was thinking for more complicated data structures where you wanted to have the flexibility of generics but still enforce them all having to be of the same type. Which is a horrible way of explaining it, but going back to the example code above, would there be a way to make it so that, when someone instantiated and instance of Test, they had to specify the type of arrayOfGenerics and be stuck with it, either through the constructor somehow, or maybe just based on the first type of value that is added?
Another way to explain it, say you were implementing your own Stack or LinkedList data structure. You want it to be a Stack of all of the same kind of Object. It can be Strings, Integers, whatever, but it has to 'pick' one and stick with it.
I think you are not using the generics effectively here. For ex. doing the following:
// With this statement, the types that can be contained in the
// instance is fixed now.
Test<String> myTest = new Test<>(10);
myTest.add("Some String"); // this will work
myTest.add(12); // this won't compile
You did declare the Test to have a generic type, but, in your original code, you didn't make use of it while constructing the instance. Which effectively results in an instance of a Test<Object>.
In Java, the generics were retrofitted, and since they didn't want to break backward compatibility, they had to allow construction of "raw" instances. However, the compiler should still give you warnings about "raw types" and probably an IDE would ask you to use #SuppressWarnings("unchecked") to make it explicit that you are using non-parameterized types.
As for enforcing the user to make sure that they are putting in correct type, the best you can do is runtime checking. That is, for ex. the constructor can take a Class as an argument. And in all the methods, you make sure that the given arguments are of correct type. But this doesn't give you compile time checking. Errors will only be known at runtime.
Another thing is that, once you have made sure that you designed your API correctly using the generics, you probably shouldn't worry about enforcing the types. If the users of the API do not want to use specific types, regardless of the warnings by the compiler, maybe they do indeed want to use a raw-type (or, maybe, they don't know better and they haven't read Effective Java). Even Java's own APIs don't make any additional efforts in that area. For ex. the following code works, and doesn't throw any exceptions at run-time:
List list = new ArrayList<String>();
list.add("xyz");
list.add(123);
You have to specify the Generic type as Test<String> myTest = new Test<>(10); for the class if you want to make the whole class
contains same type of objects.
Then your class cannot add other type of Objects. See following example.
Test<String> myTest = new Test(10);
myTest.add("Some String"); //works fine
myTest.add(12); //not works
You have set the size of the array at the Constructor. So you cannot exceed the specified size. Do following modifications to make the size random.
//constructor
public Test() {
top = 0;
arrayOfGenerics = (T[]) new Object[top];
}
//add method
public void add(T toAdd) {
top++;
T[] newArray = (T[]) new Object[top];
newArray[top - 1] = toAdd;
System.arraycopy(arrayOfGenerics, 0, newArray, 0, top - 1);
arrayOfGenerics = newArray;
}
I'm reading J. Bloch's Effective Java and now I'm at inheritance vs composition section. As far as I understood he said that inheritance is not always good.
A related cause of fragility in subclasses is that their superclass
can acquire new methods in subsequent releases. Suppose a program
depends for its security on the fact that all elements inserted into
some collection satisfy some predicate. This can be guaranteed by
subclassing the collection and overriding each method capable of
adding an element to ensure that the predicate is satisfied before
adding the element. This works fine until a new method capable of
inserting an element is added to the superclass in a subsequent
release.
But why doesn't it work? The superclass is just the Collection interface and if we add a new method we just a compile-time error. That's not harmful ever...
Suppose you have a Collection superclass in some library v1.0:
public class MyCollection {
public void add(String s) {
// add to inner array
}
}
You subclass it in order to only accept Strings that have length 5:
public class LimitedLengthCollection extends MyCollection {
#Override
public void add(String s) {
if (s.length() == 5) {
super.add(s);
}
}
}
The contract, the invariant of this class is that it will never contain a String that doesn't have length 5.
Now version 2.0 of the library is released, and you start using it. The base class is modified to:
public class MyCollection {
public void add(String s) {
// add to inner array
}
public void addMany(String[] s) {
// iterate on each element and add it to inner array
}
}
and your subclass is left unmodified. Now users of your subclass can do
LimitedLengthCollection c = new LimitedLengthCollection();
c.addMany(new String[] {"a", "b", "c"});
and the contract of your subclass is thus broken. It was supposed to only accept Strings of length 5, and it doesn't anymore, because an additional method has been added in the superclass.
The problem is not that inheritance could not work.
The problem is that with inheritance the developer can not enforce some behaviour (like the example of the collection that satisfy some predicate) .
When we create a new class rarely it really is a specialized type of another. More often it is something new that use other classes.
So rarely we need inheritance and more often we need to create a class that use other classes to so something.
The IS A vs HAS A
You have to ask yourself:
Class B IS A new sub type of Class A that do the same things of A in different ways ?
or
Class B HAS A class inside to do something different from
what A is intented to do ?
And know that more often the right answer the latter.
if we add a new mehtod we just a compile-time error
That is true only when an abstract method is added to the superclass/interface. If a non-abstract method is added, it is perfectly valid not to override that new method.
Because it (in general) will break the client code that has implemented the Collection class.
In this particular example the security will be broken because malicious users would be able to insert items by using the non yet overridden method that was added after you have shipped your code.
Basing your code on inheriting classes you do not control may bite you in the future.
In the generic class Class<T> the method getConstructors() has a return type with unknown generic type parameter instead of T. The reason for this is explainend in the javadoc.
Note that while this method returns an array of Constructor<T> objects (that is an array of constructors from this class), the return type of this method is Constructor<?>[] and not Constructor<T>[] as might be expected. This less informative return type is necessary since after being returned from this method, the array could be modified to hold Constructor objects for different classes, which would violate the type guarantees of Constructor<T>[].
A colleague of mine and I have tried to understand that explanation. In our understanding they are basically saying that it is of unknown generic type, because some caller could put other Constructor objects into that array. Did we get that right? And if so, why would someone design an API this way. Wouldn't it be better to use the specific type and trust the programmer to use the array correctly? To us it sounds a little like "We are making a worse API because the programmer using it might try something stupid". Where lies our fallacy?
The point that was mentioned by Ashu Pachauri in the comment (namely, that the array is returned for backward compatibility) is certainly valid. And in general, arrays and generics don't play together very well. (For evidence, look for all the stackoverflow questions related to "Generic Arrays"...)
Additionally, there is a rule that an API should be easy to use and hard to misuse. In this case, this is related to the Principle of least astonishment: Someone obtaining the constructors with this method could perform a perfectly legal sequence of operations on the returned array, and in the end, receive an unexpected ClassCastException. So one could say that the fact that a Constructor<?>[] array is returned aims at a "fail-fast" behavior.
An illustrative example:
import java.lang.reflect.Constructor;
public class GetConstructorsReturnType
{
public static void main(String[] args) throws Exception
{
// This causes a warning, due to the cast, but imagine
// this was possible
Constructor<DerivedA> constructorsA[] =
(Constructor<DerivedA>[])DerivedA.class.getConstructors();
// The following lines are valid due to the subtype
// relationship, but would not be valid if constructorsA
// was declared as "Constructor<?>"
Constructor<? extends Base> constructors[] = constructorsA;
constructors[0] = DerivedB.class.getConstructor();
// This causes a ClassCastException (and would also not
// be possible constructorsA was declared as "Constructor<?>"
DerivedA instance = constructorsA[0].newInstance();
}
}
class Base
{
}
class DerivedA extends Base
{
public DerivedA()
{
}
}
class DerivedB extends Base
{
public DerivedB()
{
}
}
It's the exact same reason why you are not allowed to do new Constructor<T>[], but you are allowed to do new Constructor<?>[]. You can apply your same argument and say "Wouldn't it be better to use the allow the specific type and trust the programmer to use the array correctly?" Well, Java decided no. (You can imagine that inside the getConstrucotrs method, they need to create an array of Constructor, and they cannot do new Constructor<T>[] but they can do new Constructor<?>[].)
Of course, you can make an unchecked cast of the Constructor<?>[] to the Constructor<T>[], but that will give you a warning in your code, in which case you would take responsibility for making sure it's safe. But if the getConstructors method this this unchecked cast in their code, you as the caller would never be warned about the unsafeness.