I am wondering if ya'll can help me get a better grip on generics.
I have simplified this question as much as possible.
I am trying to save the element stored in a linked list in a variable called saveIt. I know LinkedList itself uses generics, so I want my variable to take up whatever data type is in the list. I believe LinkedList itself designates that type as E, but when I try to declare a variable as 'E saveIt' I get "cannot resolve symbol E."
public class Main {
public static void main(String[] args) {
// whatever goes here ...
}
void saveElement(LinkedList input) {
E saveIt = input.getFirst();
}
}
If I make the LinkedList then I can declare a variable as 'Integer saveIt' but that isn't what I want to do.
public class Main {
public static void main(String[] args) {
// whatever goes here ...
}
void saveElement(LinkedList<Integer> input) {
Integer saveIt = input.getFirst();
}
}
I considered declaring 'Object saveIt' but I'm not sure whether that is the right approach. The concept of datatype E obviously exists in Java, and isn't quite the same as Object (or is it?), so why can't I access it (or how can I access it)?
public class Main {
public static void main(String[] args) {
// whatever goes here ...
}
void saveElement(LinkedList input) {
Object saveIt = input.getFirst();
}
}
So, basic question is if I want to have my method address whatever datatype is in the list, how do I do that?
<E> void saveElement(LinkedList<E> input) {
E saveIt = input.getFirst();
}
You need to tell Java that E is a type parameter of your method -- it's like another thing that's being passed in! -- and that the LinkedList contains elements of type E.
Related
I have the following class:
public class DataService {
static <T> void load(Structure structure, String path, DataServiceType dataService) {
//do smth
}
private interface DataServiceType<T> {
//do smth
}
private static class DataServiceInteger implements DataServiceType<Integer> {
//do smth
}
private static class DataServiceString implements DataServiceType<String> {
//do smth
}
}
I want to add there two following methods:
public static void load(Structure<Integer> structure,String path) throws IOException {
load(structure,path,new DataServiceInteger());
}
public static void load(Structure<String> structure,String path) throws IOException {
load(structure,path,new DataServiceString());
}
but both methods have same erasure. How can I achive it without changing methods names?
EDIT
I wasn't accurate. Classes implement DataServiceType have mathod :
void getDataFromString(String in, T out);
(they are paresers)
Reading from file is held in mehod static <T> void load(Structure structure, String path, DataServiceType dataService) from DataService, so M. le Rutte's solution wouldn't be good for me, because I would have to repeat myself. Is it possible to implement berry's soulution for my problem?
As you already found out, due to type erasure the runtime would not be able to distinguish between the different methods. Either the name must be different, or the arguments must be different.
However, you use a static method. My personal choice would to be to use specific instances of a DataService:
public interface DataService<T> {
Structure<T> load(Path path);
}
public StringDataService implements DataService<String> {
public Structure<String> load(Path path) {
...
}
}
public IntDataService implements DataService<Integer> {
public Structure<Integer> load(Path path) {
...
}
}
You cannot. The way type erasure works in Java, is that a 'hidden' (synthetic) method is created by the compiler during compilation which casts the object from some superclass (usually Object) to the correct type. As there are two different types in your example, the Java compiler does not know which to cast since both name and the rest of the parameters match completely.
It might be good practice to name the methods differently either way, as loading a String and loading an integer may not necessarily be handled exactly the same way. For example, you might need to load into memory a list of user input strings: in this case, the string might need to be sanitized first.
As said already, you can't do it exactly as described. However, you could do it by adding generic parameters to the load() method itself, and then making a generic DataServiceClazz type (as oppose to separate DataServiceInteger, DataServiceString classes) that implements your DataServiceType interface:
private static class DataServiceClazz<T> implements DataServiceType<T> { //Replaces DataServiceInteger, DataServiceString, etc.
//do smth
}
public static <T> void load(Structure<T> structure, String path) throws IOException {
load(structure, path, new DataServiceClazz<>());
}
This may not work, depending on your use case, since you won't be able to use different logic based on the type of T - but it's the closest pattern to what you have currently.
Java question: Can someone please tell me why the statement obj.f1(); below does not work
and how to fix it? Thanks in advance.
class X{
void f1() {
System.out.println("XXX") ;
}
}
class Y extends X {
void f1(){
System.out.println("YYY");
}
}
public class Test {
public static void main(String[] args) {
Object obj = new Y();
obj.f1(); // why this does not work?
}
}
Assuming this is java, as you have not marked it as such:
You seem to be under the impression that java is dynamically type-checked, while in reality it is statically. This means that since you have declared obj as type Object, the compiler is rejecting your code because Object has no method f1().
The compiler doesn't care what you initialize it to, a local/field's delared type (the one in front) determines what can be called on it.
To fix this, you'll need to declare obj as a Y, like so: Y obj = new Y();.
This question already has answers here:
Is there a way to compare lambdas?
(3 answers)
Closed 7 years ago.
I am not sure how I can be sure about equality/immutability of functional interface.
I guess there might be no way to assure equality when I use this syntactic sugar in java 8, please let me know any hint if you have any.
I made a short code snippet for my question.
public interface Element {
void doSomething(int a);
}
and I've tried to add instance of this interface in functional way
public class FunctionSet {
public void doubleUp(int a) {
System.out.println(a*2);
}
public void square(int a) {
System.out.println(a*a);
}
public static void main(String[] args) {
HashSet<Element> set = new HashSet<>();
FunctionSet functionSet = new FunctionSet();
set.add(functionSet::doubleUp);
set.add(functionSet::square);
System.out.println(set.add(functionSet::doubleUp));
}
}
it prints true which means there were not any equality check and also I can't remove any instance from Set once I add it.
in case I use functional interface as an argument, Is there any way that I can compare those instance somehow?
will appreciate any help, thanks in advance!
You can store your method reference into a variable:
public static void main(String[] args) {
HashSet<Element> set = new HashSet<>();
FunctionSet functionSet = new FunctionSet();
Element fn = functionSet::doubleUp;
set.add(fn);
set.add(functionSet::square);
System.out.println(set.add(fn));
}
This way it returns false.
When you create the same labmda or method reference in different code locations, it's roughly the same as you would create a new anonymous class in both positions:
public static void main(String[] args) {
HashSet<Element> set = new HashSet<>();
FunctionSet functionSet = new FunctionSet();
set.add(new Element() {
#Override
public void doSomething(int a) {
functionSet.doubleUp(a);
}
});
set.add(new Element() {
#Override
public void doSomething(int a) {
functionSet.square(a);
}
});
System.out.println(set.add(new Element() {
#Override
public void doSomething(int a) {
functionSet.doubleUp(a);
}
}));
}
So every time it's a different object, though it may look the same. For every encountered method reference separate anonymous class is created at the runtime:
Element e1 = functionSet::doubleUp;
Element e2 = functionSet::doubleUp;
System.out.println(e1.getClass());
System.out.println(e2.getClass());
The output will be like this:
class FunctionSet$$Lambda$1/918221580
class FunctionSet$$Lambda$2/1554547125
So practically it's two distinct objects of two distinct classes. It would be quite difficult to conclude that they do the same thing without comparing their bytecode. Also note that they both capture the functionSet variable, so it should also be ensured that it wasn't changed between two method references.
The only workaround I can think up is to declare all the method references as constants in your code and later reference them instead of using method references directly:
public static final Element FN_DOUBLE_UP = new FunctionSet()::doubleUp;
public static final Element FN_SQUARE = new FunctionSet()::square;
public static void main(String[] args) {
HashSet<Element> set = new HashSet<>();
set.add(FN_DOUBLE_UP);
set.add(FN_SQUARE);
System.out.println(set.add(FN_DOUBLE_UP));
}
I have a function e.g.
helloworld(list<object> names)
I have the following code :
List<CustomClass> newMe = new ArrayList<CustomClass>();
Now, if i want to pass newMe into helloworld(newMe);. This is not possible because im down casting. How can i overcome this issue? Do i downcast my list to (Object) and then try to upcast it? is there another way? would appreciate an example.
thanks
Change the definition of helloworld to
public void helloworld(List<?> names) {
//method implementation...
}
Take into account that your method won't be able to add or remove elements from the list parameter.
Just use a ? as generic type in your parameter list. Example:
public class Foobar {
public static void helloworld(List<?> names) {
}
public static void main(String[] args) {
List<CustomClass> newMe = new ArrayList<>();
helloworld(newMe);
}
}
I have an assignment and I'm stuck. The assignment is to write a generic class for this method:
public static void main(String[] args) {
ValueStore<Object> myStore1 = new ValueStore<Object>();
myStore1.set("Test");
myStore1.get();
///
ValueStore<Object> myStore2 = new ValueStore<Object>();
myStore2.set(myStore1);
myStore1 = myStore2.get();
}
I've come this far.
public class ValueStore<T> {
private T x;
public void set(T x) {
System.out.println(x);
}
public T get () {
return x;
}
}
I am able to print out mystore.set "test", but not the myStore2.set. And I don't understand why my teacher passed in a reference variable as a argument. When I do that I get ValueStore#15db9742 in the console. Or maybe thats the point?
Can someone explain why it says myStore2.set(myStore1);
myStore1 = myStore2.get(), what it should print and the logic behind it?
Thank you in advance. And sorry if my text is all messy. First time here.
I think currently you are just missing a line from your set() method, as in
public void set(T x) {
System.out.println(x);
this.x = x;
}
So that you would actually store the object.
I have commented a bit more to explain. The main point is that you can give a type to your ValueStore (in this example, String). This makes the typesystem aware that when you call get() on the valuestore, it gets a string in return. This is actually the entire point of generics. If you simply put object, only you know that the get method will return a String so you would have to cast it (as in the second example).
public static void main(String[] args) {
// Type your store with String, which is what generics is about.
ValueStore<String> myStore1 = new ValueStore<String>();
// Store a string in it.
myStore1.set("Test");
// Get the object, and the typesystem will tell you it's a string so you can print it.
System.out.println(myStore1.get());
///
ValueStore<Object> myStore2 = new ValueStore<Object>();
// Store your store.
myStore2.set(myStore1);
// The type system only knows this will return an Object class, as defined above.
// So you cast it (because you know better).
myStore1 = (ValueStore<String>) myStore2.get();
System.out.println(myStore1.get());
}
public class ValueStore<T> {
private T x;
public void set(T x) {
this.x = x;
}
public T get () {
return x;
}
}
This code prints the following:
test
test