Please have a look at following code :
import java.util.ArrayList;
import java.util.List;
class Main{
public static <T> List<T> modifiedList(final List<T> list){
return new ArrayList<T>(){
#Override
public boolean add(T element){
super.add(element);
return list.add(element);
}
};
}
public static void main(String[] args) {
List<String> originalList=new ArrayList<String>();
List<String> duplicateList=modifiedList(originalList);
originalList.add("1");
originalList.add("2");
originalList.add("3");
System.out.println(originalList+" "+duplicateList);
duplicateList.add("4");
duplicateList.add("5");
duplicateList.add("6");
System.out.println(originalList+" "+duplicateList);
}
In the above code, the instance of an anonymous inner class declared in the method modifiedList() is able to access the parameter passed to that method. AFAIK Java creates a separate bytecode file for inner classes.
Can anyone explain how these local variable bindings are handled by Java at the bytecode level? I mean, how exactly does Java keep track of the reference to the object passed as a parameter to that method?
Any help would be greatly appreciated!
[Sorry for my poor English! If you understand my question, please edit this post and remove the grammatical errors. Thanks!]
Essentially the code is rewritten by the complier as (note I didn't try to compile it..., might have errors):
class Main$1<T>
extends ArrayList<T>
{
private final List<T> list;
Main$1(final List<T> a)
{
list = a;
}
#Override
public boolean add(T element)
{
super.add(element);
return list.add(element);
}
}
and
class Main{
public static <T> List<T> modifiedList(final List<T> list)
{
return new Main$1<T>(list);
}
public static void main(String[] args)
{
List<String> originalList=new ArrayList<String>();
List<String> duplicateList=modifiedList(originalList);
originalList.add("1");
originalList.add("2");
originalList.add("3");
System.out.println(originalList+" "+duplicateList);
duplicateList.add("4");
duplicateList.add("5");
duplicateList.add("6");
System.out.println(originalList+" "+duplicateList);
}
import java.util.ArrayList;
import java.util.List;
class Main{
public static <T> List<T> modifiedList(final List<T> list){
return new ArrayList<T>(){
private List<T> originalList=list;
#Override
public boolean add(T element){
super.add(element);
return originalList.add(element);
}
};
}
public static void main(String[] args) {
List<String> originalList=new ArrayList<String>();
List<String> duplicateList=modifiedList(originalList);
originalList.add("1");
originalList.add("2");
originalList.add("3");
System.out.println(originalList+" "+duplicateList);
duplicateList.add("4");
duplicateList.add("5");
duplicateList.add("6");
System.out.println(originalList+" "+duplicateList);
}
}
Java allows such a strange thing just in order to make things easier for programmers.
Both the codes are semantically same and boil down to the identical bytecode.
Related
I get error: Cannot resolve method 'sort(java.util.List<T>, boolean)' in case:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Arrays;
public class MyClass {
public static void main(String args[]) {
MyArray arr = new MyArray();
arr.set("b");
arr.set("a");
arr.sort();
}
}
class MyArray<T> {
List<T> myList;
MyArray(){
myList = new ArrayList<T>();
}
public void set(T val) {
myList.add(val);
}
public void sort() {
ListFunctions.sort(myList, true);
System.out.println(myList);
}
}
class ListFunctions {
public static <T extends Comparable<T>> void sort(List<T> array, boolean ascending){
if (ascending){
array.sort(null);
}
else {
array.sort(Collections.reverseOrder());
}
}
}
Problem is in line ListFunctions.sort(myList, true). I can't convert this way (List)myList, because meaning of constraint T extends Comparable<T> is lost.
I see examples of code where T need to implement Comparable interface, but in my case I can't specify class. I implicitly use List<String>
Your class
class MyArray<T>
Should simply be
class MyArray<T extends Comparable<T>>
If you don't do that, you may potentially pass an out of bound type to your utility function which the compiler will reject accordingly.
MyArray arr = new MyArray();
will hence become
MyArray<String> arr = new MyArray<>();
Please explain to me why I have one example that compiles and the other one doesn't compile
This is example that compiles
import java.util.ArrayList;
public class MyClass1 <T> {
public ArrayList<MyClass1<?>> lst;
public MyClass1()
{
lst = new ArrayList<MyClass1<?>>();
}
public static void main(String[] args) {
M(new MyClass1<Double>());
}
public static <T1> void M(MyClass1<T1> t1)
{
var d0 = new MyClass1<Double>();
d0.lst.add(t1);
}
}
But that doesn't compiles
import java.util.ArrayList;
public class MyClass1 <T> {
public ArrayList<?> lst;
public MyClass1()
{
}
public static void main(String[] args) {
M(Double.valueOf(1.1));
}
public static <T1> void M(T1 t1)
{
var d0 = new MyClass1<Double>();
d0.lst.add(t1); // error — java: incompatible types: T1 cannot be converted to capture#1 of ?
}
}
Why in the first case I have wildcard, and everything is OK, but in the second case I have wildcard and it doesn't compile
The second snippet can not be compiled because the generics in Java are about type safety(type guarantee). Thus, if we are declearing List<Integer> the compiler is convinced, that the list will contain only integers. But when we have List<?> (read this like list of some type), compiler cant predict what will the list contain. Today we are adding Integers, tomorrow somebody else will add a ServerSocket. In runtime if we will try to get a value from such a list, we will get a ClassCastException. That is why this code cant even be compiled - to prevent such situations. Read about type erasure and bridge methods.
In your concrete case, i guess, you would like to have something like this:
public class MyClass<T> {
public ArrayList<T> lst;
public static void main(String[] args) {
var obj = new MyClass<Double>();
obj.foo(1.1);
}
public void foo(T s) {
lst.add(s);
}
}
The question mark also can be useful:
static void m(List<? extends Shape> list) {
for (Shape el : list) {
// we are not quite interested what type is this list of.
}
}
And the usage example:
m(new ArrayList<Circle>());
m(new ArrayList<Shape>());
I don't understand why the following code works:
import java.util.ArrayList;
import java.util.Collection;
public class Main {
public static void main(String[] args) {
Integer[] arr=new Integer[]{1,2,3};
ArrayList<Object> al=new ArrayList<>();
addToCollection(arr, al);
}
static <T> void addToCollection(T[] a, Collection<T> c)
{
for(T o:a)
c.add(o);
}
}
Shouldn't it be:
...
static <T> void addToCollection(T[] a, Collection<? super T> c)
...?
Shouldn't the type T be the same during the call?
As said in the comments, my question is "which type is inferred for T". Since the code is working, I assume the "higher" type in the hierarchy is inferred.
Both arr and al are subtypes of Object, so that's what you're getting. If you change your addToCollection function to have a return type, this happens:
public static class Main {
public static void main(String[] args) {
Integer[] arr=new Integer[]{1,2,3};
ArrayList<Object> al=new ArrayList<>();
Collection<Object> objects = addToCollection(arr, al); // Compiles
Collection<Integer> numbers = addToCollection(arr, al); // Doesn't compile
}
static <T> Collection<T> addToCollection(T[] a, Collection<T> c)
{
for(T o:a) // Behold the side effect
c.add(o);
return c;
}
}
I'm not entirely comfortable with generics and thus haven't found a solution to this yet. I have these three methods:
public static List<ObjectA> objectAAsList(ObjectA ... items) {
return new ArrayList<>(Arrays.asList(items));
}
public static List<ObjectB> objectBAsList(ObjectB ... items) {
return new ArrayList<>(Arrays.asList(items));
}
public static List<ObjectC> objectCAsList(ObjectC ... items) {
return new ArrayList<>(Arrays.asList(items));
}
How can I create a single method that takes a vararg of T (or something) and creates an ArrayList of it?
Just replace your type with a type variable:
public static <T> List<T> genericAsList(T ... items) {
return new ArrayList<>(Arrays.asList(items));
}
Note that you could look at how Arrays.asList is declared, since it does largely the same thing, from a type perspective.
I think a Function is a better approach than a static method. You can define a Function :
public class VarArgsToList<T> implements Function<T[], List<T>> {
#Override
public List<T> apply(final T... items) {
return new ArrayList<>(Arrays.asList(items));
}
}
and apply it wherever:
public static void main(final String... arg) {
...
final List<String> list1 = new VarArgsToList<String>().apply(arg);
...
final List<MyObject> list2 = new VarArgsToList<MyObject>().apply(myObject1, myObject2, myObject3);
...
}
What does the following code look at runtime after type erasure:
public class Test<T> {
T lst;
List<T> list1;
void meth() throws InstantiationException, IllegalAccessException{ T res = (T)lst.getClass().newInstance();}
static <S> void meth(S t){}
}
class TestUse{
public static void main(String[] args) {
Test<Integer> gint = new Test<Integer>();
Test<String> gstr = new Test<String>();
gint.meth();
gstr.meth();
}
The following piece of code would not work:
T res = (T)lst.getClass().newInstance();
Since T is of type Object at runtime in all the possible cases.
One workaround is using generics with inheritance:
public abstract class Generic<T> {
public abstract Class<T> getConcreteClass();
public void doSomething() {
Class<T> clazz = getConcreteClass();
T t = clazz.newInstance();
doSomethingWithT();
}
}
public class ConcreteClass extends Generic<YourObject> {
public Class<YourObject> getConcreteClass() {
return YourObject.class;
}
}
I think it looks like this
public class Test {
Object lst;
List list1;
void meth() throws InstantiationException, IllegalAccessException{
Object res =lst.getCla ss().newInstance();
}
static void meth(Object t){
}
}
class TestUse{
public static void main(String[] args) {
Genrics gint = new Genrics();
Genrics gstr = new Genrics();
gint.meth();
gstr.meth();
}
although I do not think this would compile as it stands. There is no Genrics class - or is that the Test class.
Assuming that your Genrics class is actually your Test class, it should be equivalent to something like this:
public class Genrics {
Object lst;
List list1;
void meth() throws InstantiationException, IllegalAccessException {
Object res = lst.getClass().newInstance();
}
static void meth(Object t){}
}
class TestUse{
public static void main(String[] args) {
Genrics gint = new Genrics();
Genrics gstr = new Genrics();
gint.meth();
gstr.meth();
}
Whether my assumption is right or wrong, the way to think about this is that all generic type parameters are simply erased from your code and replaced with the most specific bound for the type parameter (Object in your case) or nothing where appropriate. Casts are introduced where needed.
The details of how type erasure works are spelled out in the Java Language Specification.