I'm new to Java, and I'm trying to figure out one thing about generics.
If I declare a method like
public <T> List<T> toList(final T... arr) { ... }
Can I return both ArrayList and LinkedList?
Or for example, if I have declare a method like
public <T> T[] toArray(final List<T> l) { ... }
Can I pass both ArrayList and LinkedList as argument and it'll works good?
If this is right, does it works with all objects too? So, if I create a class and I extend it more times, can I use the top class as arg of method, but the pass its subclasses when I call it?
It's not about generics only. You can assign any object to a variable of its parent class.
Object o = 5; // It's valid
If you pass a LinkedList or ArrayList to the toArray()method, it doesn't matter. It will be automatically converted to List. Similarly, if you return a LinkedList from the toArray() method, it doesn't matter. It will be converted to List.
But one thing to keep in mind is if you pass a LinkedList, it will get converted to List and you will be able to use only the methods of the List interface.
List list = new LinkedList();
list.addFirst(1); // Invalid
Related
I am wanting to take user data in one method and pass that data to an "add" method, as to add said data to a generic arraylist. However I receive the following error message...
add (T) in List cannot be applied to (java.lang.String)
I'm not sure if this is a static problem or not...
My simplified code:
List<T> list = new ArrayList<T>();
void dataCollection() {
Scanner scan = new Scanner(System.in);
System.out.println("type data");
add(scan.next());
}
void add(T t) {
list.add(t);
}
If you want to add Strings to your List, define it as a List<String>, not List<T>.
A List<T> might be (depending on how your class is instantiated), for example, a List<Integer>, which can't hold Strings.
If you truly need your List to be generic, you shouldn't be adding Strings to it.
I am attempting to convert an ArrayList of class SomeClass to an ArrayList of class Object. This new ArrayList of Object will then be passed to a function. I currently have done the following:
// convert ArrayList<SomeClass> to generic ArrayList<Object>
Object[] objectArray = someClassList.toArray();
ArrayList<Object> objects = new ArrayList<Object>();
for (int i = 0; i < objectArray.length; i++) {
objects.add(objectArray[i]);
}
someFunction(objects);
public void someFunction(ArrayList<Object> objects) {
// do something with objects
}
Is there a more efficient or "standard" way of doing this? Is what I am doing "wrong" in the first place?
The purpose of converting it to ArrayList of class Object is that I have created an external library to process ArrayList of generic Objects.
If you are able to change the function's signature to taking an ArrayList<? extends Object> objects or an ArrayList<?> objects (or even better, List instead of ArrayList), you will be able to pass your ArrayList<SomeClass> directly.
The reason an ArrayList<SomeClass> is not an ArrayList<Object> is that an ArrayList<Object> would accept that you add() any kind of Object into it, which is not something you can do with an ArrayList<SomeClass>. On the other hand, an ArrayList<? extends Object> will allow you to retrieve elements from the list, but not add elements, so ArrayList<SomeClass> can safely be assigned to it.
Since you created the external library, I think it would be easier to modify the function signature to accept lists of any type. This can be accomplished using the unbounded wildcard ?:
public static void someFunction(List<?> objects) {
// whatever
}
Then you don't need to make any conversions to call it:
public static void main(String[] args) {
List<String> words = new ArrayList<>();
someFunction(words);
}
Also, unless you have a good reason not to, it would be better to accept any List in someFunction instead of limiting your input to ArrayLists. This makes your code more flexible and easier to change in the future.
A simple way to convert a List<SubFoo> to a List<Foo> is to use Collections.unmodifiableList(listOfSubFoos), which is perfectly type-safe and actually enforces that you can't do anything bad with it (like adding a DifferentSubFoo).
It is possible to transform the type parameters of a type in arbitrary ways with two casts:
ArrayList<SomeClass> l1 = ...;
ArrayList<Object> l2 = (ArrayList<Object>) (Object) l1;
But, as Aasmund Eldhuset also says in his answer: This is probably not a good idea! It is better to give a more suitable type to l2 instead, like ArrayList<?>.
This code gives you an compile warning saying Type safetyThat: Unchecked cast from Object to ArrayList<Object> for a reason. If for example a String is added to l2 and then someone reads l1 and expects a SomeClass they will get a very unexpected ClassCastException.
Here is my method. I want to return a collection of strings from a Java method. I would like for the calling code to decide whether it wants to implement this collection as a Vector or a LinkedList or an ArrayList or something else that implements the List interface.
public List<String> findAvailableLanguages() {
Iterator<Map.Entry<String, String>> it = this.iterator();
List<String> list = new ArrayList<String>();
while (it.hasNext()) {
Map.Entry<String, String> n = it.next();
list.add(n.getKey());
}
...
However, I must instantiate a concrete class object inside of the method in order to build the collection. What do I now return that will be compatible with any class that implements List?
Is this possible?
It's more effective for the callers if you allow a List to be passed in the filling process instead of initiating your own. It will also make for code that's easily unit-testable, as this does the pattern known as Dependency Injection.
public List<String> populateWithAvailableLanguages(List<String> list) {
Iterator<Map.Entry<String, String>> it = this.iterator();
// List<String> list = new ArrayList<String>();
while (it.hasNext()) {
Map.Entry<String, String> n = it.next();
list.add(n.getKey());
}
}
Now the implementation of the List can be specified by the caller:
List<String> availableLanguages = new ArrayList<>();
Localizer.populateWithAvailableLanguages(availableLanguages);
In short, returning an ArrayList object type that can be cast into any other object type that implements List is not possible. The elements can be traversed and added to another object type but the collection type itself cannot be cast.
I'll explain why. When you say,
List<String> list = new ArrayList<String>();
the reference type of 'list' is List and the object type is ArrayList. The 'list' object that you now have gives you access to all the methods of the List interface but not to the other methods that ArrayList has although the list object can see them (kind of like a narrowing conversion). You can cast this list object back to an ArrayList object and that would work because the list instance anyway could see the methods that ArrayList had and hence casting this back will work (kind of like a widening conversion back to the original width).
But if you were to cast it to one of the other classes implementing the List interface like LinkedList or Vector or Stack, what will happen? The list instance does not know how the other methods present in LinkedList, Vector or Stack are implemented (as they are not in ArrayList). So it's kind of like a conversion where you do not know what needs to be done. So it will throw back a compiler error.
Extending this, you can see, if you had:
List<String> list = new LinkedList<String>();
now, casting the list back to a LinkedList will work but not back to an ArrayList.
Your method signature should be as below
Public Collection(? super List) findAvailableLanguages(){}
The method signature looks like this:
public void addThemAll(Collection<? extends T> c)
Which essentially just adds every element of the collection to my LinkedList. But I keep trying to feed this method an Array or a Linked List and I always get an error. For example:
double[] myarray = new double[]{3.4, 4.5, 8.6};
mylist.addThemAll(myarray);
I'm sure this is something straightforward, but I can't find an example online that just passes an array/linked list into a method like this.
Your code has two problems:
An array is not a collection. It does not extend Collection. Therefore, you can't pass it into a method whose signature specifies a collection parameter.
You have not defined <T> (or, at least, you have not shown us where you are defining <T>). You can either define <T> in your class, or in your method signature.
To define it in your class, do it like this:
public class MyClass<T> {
// contents
}
To define <T> in your method, do it like this:
public <T> void addThemAll(Collection<? extends T> c) {
// method logic
}
For what you are doing, this would work:
List<Double> myArray = Arrays.asList(3.4, 4.5, 8.6);
mylist.addThemAll(myarray);
The reason being is that you are passing in a list (which is a collection). Currently you are passing in an Array, which is not a collection.
To pass in the array to collection:
Double[] myarray = new Double[]{3.4, 4.5, 8.6};
mylist.addThemAll(Arrays.asList(myarray));
if you don't want it as list but want it as LinkedList or etc
LinkedList<Double> linkedlist = new LinkedList(Arrays.asList(myarray));
mylist.addThemAll(linkedlist);
if you want to use set or treeset
TreeSet <Double> treeset = new TreeSet(linkedlist);
Difference between set and list is that set does not have duplicate and not ordered, and list is ordered but contains duplicates.
After you pass in to your method:
public void addThemAll(Collection<? extends T> c)
if(c instanceof LinkedList){
LinkedList a = (LinkedList) c //you can invoke methods from LinkedList
....
}
I have the class:
class SomeClass<T extends SomeInterface>{
private T[] myArray;
public SomeClass()
{
// I want to initialize myArray in here to a default size of 100
myArray = new T[100]; // this gives an error
}
}
I know I can fix that by requiring a parameter in the constructor as:
class SomeClass<T extends SomeInterface>{
private T[] myArray;
public SomeClass(Class<T> clazz)
{
myArray= (T[]) Array.newInstance(clazz, 100);
}
}
but it makes no scene having to pass the generic parameter twice.
in other words in order to instantiate an object from the class SomeClass I will have to do something like:
SomeClass<SomeOtherClass> obj =
new SomeClass<SomeOtherClass>(SomeOtherClass.class);
I program in c# and Java does not seem to be friendly. I don't even understand why it is not possible to cast Object[] array to SomeOtherClass[] array. In c# that will be possible...
so my question is how can I avoid having to pass the SomeOtherClass.class parameter in order to be able to construct an array of the generic type in the constructor of the class...
While Shakedown listed the fix to your problem, allow me to explain why it is not typesafe to create a generic array.
I will illustrate why with an example from Effective Java 2nd Ed.
// Why generic array creation is illegal - won't compile!
List<String>[] stringLists = new List<String>[1]; // (1)
List<Integer> intList = Arrays.asList(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)
Let’s pretend that line 1, which creates a generic array, is legal.
Line 2 creates and initializes a List<Integer> containing a single
element.
Line 3 stores the List<String> array into an Object array
variable, which is legal because arrays are covariant.
Line 4 stores the List<Integer> into the sole element of the Object array, which
succeeds because generics are implemented by erasure: the runtime type
of a List<Integer> instance is simply List, and the runtime type of a
List<String>[] instance is List[], so this assignment doesn’t generate
an ArrayStoreException. Now we’re in trouble. We’ve stored a
List<Integer> instance into an array that is declared to hold only
List<String> instances.
In line 5, we retrieve the sole element from
the sole list in this array. The compiler automatically casts the
retrieved element to String, but it’s an Integer, so we get a
ClassCastException at runtime. In order to prevent this from
happening, line 1 (which creates a generic array) generates a
compile-time error.
Yes, you would have to pass in the .class like that in order to make this work.
You could avoid all of this and just use an ArrayList<T> instead. When you need it in the form of an array you can use: (T[]) myArrayList.toArray()
All and every type in java is a subclass of Object class so this can be achieved using this.
public StackArray(int size){
dataStack = (T[])new Object[size];
}