I'm messing around with lambdas and I'm trying to create a generic way to form a predicate for a class on a field. Here's some code to illustrate:
public class A {
private String b;
private String c;
public A(String b, String c) {
this.b = b;
this.c = c;
}
public String getB() {
return b;
}
public String getC() {
return c;
}
}
public class Main {
public static void main(String[] args) {
List<A> list = Arrays.asList(new A("aa","bb"),new A("aaC","bb"));
Test test = new Test();
test.setList(list);
test.createPred("aa");
}
}
public class Test {
private List<A> list;
public void setList(List<A> list) {
this.list = list;
}
public Predicate<A> createPred(String query) {
return new Predicate<A>() {
#Override
public boolean test(A t) {
return t.getB().equals(query);
}
};
}
public List<A> search(Predicate<A> a) {
return list.stream().filter(a).collect(Collectors.toList());
}
}
How can I write createPred so it can take a field? I want the method to be "field-generic" I suppose. I'm guessing using java reflection here is not a good idea.
You can make your method take a Function and a T as the query field.
public static void main(String[] args) {
List<A> list = Arrays.asList(new A("aa", "bb"), new A("aaC", "bb"));
Test test = new Test();
test.setList(list);
test.createPred("aa", A::getB);
}
public static class Test {
private List<A> list;
public void setList(List<A> list) {
this.list = list;
}
public <T> Predicate<A> createPred(T query, Function<A, T> f) {
return new Predicate<A>() {
#Override
public boolean test(A x) {
return f.apply(x).equals(query);
}
};
}
public List<A> search(Predicate<A> a) {
return list.stream().filter(a).collect(Collectors.toList());
}
}
Related
I have some generated code (i.e. it cannot be changed) that looks something like this.
class Generated1 {
public String getA() {
return "1";
}
public void setB(String b) {
}
public void setC(String c) {
}
public void setD(String d) {
}
}
class Generated2 {
public String getA() {
return "2";
}
public void setB(String b) {
}
public void setC(String c) {
}
public void setD(String d) {
}
}
I am exploring these objects by reflection. None of them implement any common interface but there's many of them and I want to treat them as if they implement:
interface CommonInterface {
String getA();
void setB(String b);
void setC(String c);
void setD(String d);
}
It certainly should be possible. This is considered perfectly good code
class CommonInterface1 extends Generated1 implements CommonInterface {
// These are perfectly good classes.
}
class CommonInterface2 extends Generated2 implements CommonInterface {
// These are perfectly good classes.
}
I suppose what I'm looking for is something like:
private void doCommon(CommonInterface c) {
String a = c.getA();
c.setB(a);
c.setC(a);
c.setD(a);
}
private void test() {
// Simulate getting by reflection.
List<Object> objects = Arrays.asList(new Generated1(), new Generated2());
for (Object object : objects) {
// What is the simplest way to call `doCommon` with object here?
doCommon(object);
}
}
My question: How do I treat an object that doesn't implement an interface but actually has all the code to do so as if it does implement the interface.
I want to replace
private void doCommon(Generated1 c) {
String a = c.getA();
c.setB(a);
c.setC(a);
c.setD(a);
}
private void doCommon(Generated2 c) {
String a = c.getA();
c.setB(a);
c.setC(a);
c.setD(a);
}
...
with
private void doCommon(CommonInterface c) {
String a = c.getA();
c.setB(a);
c.setC(a);
c.setD(a);
}
I know I can use a Proxy like this but I'd really prefer to use something better.
private void test() {
// Simulate getting by reflection.
List<Object> objects = Arrays.asList(new Generated1(), new Generated2());
for (Object object : objects) {
// What is the simplest way to call `doCommon` with object here?
doCommon(adapt(object));
}
}
private CommonInterface adapt(Object o) {
return adapt(o, CommonInterface.class);
}
public static <T> T adapt(final Object adaptee,
final Class<T>... interfaceToImplement) {
return (T) Proxy.newProxyInstance(
adaptee.getClass().getClassLoader(),
interfaceToImplement,
// Call the equivalent method from the adaptee.
(proxy, method, args) -> adaptee.getClass()
.getMethod(method.getName(), method.getParameterTypes())
.invoke(adaptee, args));
}
If you're using reflection, you don't need the two CommonInterfaceX classes, you can use a proxy implementing CommonInterface:
public class Wrapper implements InvocationHandler {
private final Object delegate;
public static <T> T wrap(Object obj, Class<T> intf) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Object proxy = Proxy.newProxyInstance(cl, new Class<?>[] {intf},
new Wrapper(obj));
return intf.cast(proxy);
}
private Wrapper(Object delegate) {
this.delegate = delegate;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Method dmethod = delegate.getClass().getMethod(
method.getName(), method.getParameterTypes());
return dmethod.invoke(delegate, args);
}
}
You can use this class as follows:
List<Object> objects = Arrays.asList(new Generated1(), new Generated2());
for (Object object : objects) {
CommonInterface proxy = Wrapper.wrap(object, CommonInterface.class);
doCommon(proxy);
}
UPDATE: note that the same Wrapper class works with any interface.
There's no way to achieve a static type relationship between Generated1 and Generated2.
Even if you created CommonInterface1 and CommonInterface2, you still wouldn't be able to statically use a Generated1 object as a CommonInterface1 because new Generated1() is not a CommonInterface1 (and will never become one)
By far the simplest solution is to change your code generation to add the CommonInterface to Generated1 and Generated2.
If that's absolutely impossible, the only other way to avoid this code duplication is to go for reflection.
You can do it manuallly by reflection.
public class Generated {
public String getA() {
return "A";
}
public String sayHello(String name) {
return "hello " + name;
}
}
public class Helper {
private static final String METHOD_NAME = "getA";
private static final String METHOD_WITH_PARAM_NAME = "sayHello";
public static void main(String[] args) throws Exception {
Generated generated = new Generated();
accessMethod(generated);
accessMethodWithParameter(generated);
}
private static void accessMethod(Generated g) throws Exception {
Method[] methods = g.getClass().getDeclaredMethods();
for(Method method : methods) {
if(isCommonMethod(method)) {
String result = (String) method.invoke(g);
System.out.println(METHOD_NAME + "() = " + result);
}
}
}
private static boolean isCommonMethod(Method m) {
return m.getName().equals(METHOD_NAME) && m.getReturnType().equals(String.class);
}
private static void accessMethodWithParameter(Generated g) throws Exception {
Method[] methods = g.getClass().getDeclaredMethods();
for(Method method : methods) {
if(isCommonMethodWithParameter(method)) {
String result = (String) method.invoke(g, "Max");
System.out.println(METHOD_WITH_PARAM_NAME + "(\"Max\") = " + result);
}
}
}
private static boolean isCommonMethodWithParameter(Method m) {
return m.getName().equals(METHOD_WITH_PARAM_NAME) &&
m.getReturnType().equals(String.class) &&
m.getParameterTypes().length == 1 &&
m.getParameterTypes()[0].equals(String.class);
}
}
Output is
getA() = A
sayHello("Max") = hello Max
If you want to replace as your comment. I think you can do it easily
First, you create interface CommonInterface
interface CommonInterface {
String getA();
void setB(String b);
void setC(String c);
void setD(String d);
}
After that, you create 2 class Generated1 and Generated2 inherited CommonInterface
class Generated1 implements CommonInterface {
#overide
public String getA() {
return "1";
}
#overide
public void setB(String b) {
}
#overide
public void setC(String c) {
}
#overide
public void setD(String d) {
}
}
class Generated2 implements CommonInterface {
#overide
public String getA() {
return "2";
}
#overide
public void setB(String b) {
}
#overide
public void setC(String c) {
}
#overide
public void setD(String d) {
}
}
the code below works fine:
public class ICopyableTest {
private interface ICopyable<T extends ICopyable<? extends T>> {
void copyFrom(T original);
}
private interface IVal<T> extends ICopyable<IVal<? extends T>> {
T getV();
}
private static class Val<T> implements IVal<T> {
private T v;
public T getV() {
return v;
}
public Val(final T v) {
this.v = v;
}
#Override public void copyFrom(final IVal<? extends T> original) {
v = original.getV();
}
}
private static class StrVal extends Val<String> {
public StrVal(final String v) {
super(v);
}
}
public static void main(String[] args) {
Val<Object> o1 = new Val<>(new Object());
Val<String> o2 = new Val<>("qwe");
StrVal o3 = new StrVal("zxc");
o1.copyFrom(o2); // that's the point
o1.copyFrom(o3);
o2.copyFrom(o3);
o3.copyFrom(o2);
Val<Object> toObj = (Val<Object>) (Val<?>) o2; // [1]
}
}
Basically i have ICopyable interface with provides copying functionality, IVal which adds storage of value above, and two example classes which implement Val. The point of <? extends T> is to provide covariant argument to the CopyFrom method, so you could do o1.copyFrom(o2) etc.
So it all works fine i guess.
Now let's say i want to have another parameterized over ICopyable or IVal class:
private static class Bla<T extends ICopyable<T>> {
final T value1;
final T value2;
public Bla(final T value1, final T value2) {
this.value1 = value1;
this.value2 = value2;
}
void letsCopy() {
value1.copyFrom(value2);
value2.copyFrom(value1);
}
}
Now why can't i instantiate it with any of the following?
new Bla<StrVal>(o3, o3);
new Bla<Val<Object>>(o1, o1);
new Bla<Val<String>>(o2, o2);
To be honest i'm a bit lost here myself and that's why i'm exploring it. There's a separate very important question of why working with generics is SO draining, when even after 5+ years working with java i can't figure out those things without half an hour meditation - am i just dumb?
I just want to have a parameterized class which will allow me to work with IVal/ICopyable values, note that it should be parameterized class, not individual method, so you can store the instances of those values in fields, for example.
I would have done that this way
public class Test {
interface Copyable<T> {
void copyFrom(Copyable<T> v);
T getV();
}
interface Val<T> extends Copyable<T> {
// Do not know if this is usefull
}
abstract class AbstractVal<T> implements Val<T> {
T value;
public AbstractVal(T val) {
this.value = val;
}
}
class StrVal extends AbstractVal<String> {
public StrVal(String o3) {
super(o3);
}
#Override
public void copyFrom(Copyable<String> v) {
this.value = v.getV();
}
#Override
public String getV() {
return this.value;
}
}
class Bla<T extends AbstractVal<S>, S> {
final T value1;
final T value2;
public Bla( T value1, final T value2) {
this.value1 = value1;
this.value2 = value2;
}
void letsCopy() {
value1.copyFrom(value2);
}
}
void test() {
StrVal o1 = new StrVal("qwe");
StrVal o2 = new StrVal("qwe2");
StrVal o3 = new StrVal("zxc");
Bla tester = new Bla<StrVal, String>(o1, o2);
tester.letsCopy();
System.out.println(tester.value1.getV());
}
public static void main(String[] args) {
Test t = new Test();
t.test();
}
}
Maybe a little complicated for what it's doing but I think it's the idea..
Given the following Java code:
public class Test {
public static class A<T> {
private T t;
public A(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public static class B<T> {
private T t;
public B(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public static class F<T> {
private T t;
public F(T t) {
this.t = t;
}
public A<B<T>> construct() {
return new A<>(new B<>(t));
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
public static void main(String[] args) {
F<?> f = new F<>(0);
// 1: KO
// A<B<?>> a = f.construct();
// 2: KO
// A<B<Object>> a = f.construct();
// 3: OK
// A<?> a = f.construct();
}
}
In the main method of the Test class, what is the correct type of a variable that will receive the result of f.construct() ?
This type should be something like A<B<...>> where ... is what I'm looking for.
There are 3 commented lines of code above which represent my attempts to solve this problem.
The first and second lines aren't valid.
The third is but I loose the B type information and I have to cast a.getT().
A<? extends B<?>> a = f.construct(); is the right syntax, as stated by Paul Boddington.
I have deal with one problem while accessing arraylist element in another class. I have 2 classes: class A and class B.
class A {
private ArrayList<String> temp=new ArrayList<String>();
temp.add("abc");
temp.add("XYZ");
public ArrayList<String> getTemp() {
return this.temp;
}
}
public class B
{
private A a=null;
public b(A aa)
{
this.a = aa;
}
System.out.printLn(a.getTemp.size());//output is 2
System.out.printLn(a.getTemp.get(0));//null
}
Why it is giving me null? Please give brief explanation of this.
Here is a working version of what you are trying to achieve:
A.java
In the A class, you should be adding elements to your ArrayList in the constructor:
public class A {
private ArrayList<String> temp=new ArrayList<String>();
public A() {
temp.add("abc");
temp.add("XYZ");
}
public ArrayList<String> getTemp() {
return this.temp;
}
}
B.java
The constructor name should match the class's:
public class B {
private A a=null;
public B(A aa)
{
this.a = aa;
}
}
App.java
public class App {
public static void main(String[] args) {
A a = new A();
System.out.println(a.getTemp().size());
System.out.println(a.getTemp().get(0));
}
}
Output:
2
abc
Your current code won't even compile.
Furthermore, I can guarantee 100% that if by some magic your code were to compile the output of the first printLn would in no way be 2. It would be null. `
**First Of All Your Code Is Not Impossible to run**
You Can't assign value to instance variable directly in side of class without constructor or method so your modified class A must be like
**A.java**
class A {
private ArrayList<String> temp=new ArrayList<String>();
public A()
{
temp.add("abc");
temp.add("XYZ");
}
public ArrayList<String> getTemp()
{
return this.temp;
}
}
OR Like
class A {
private ArrayList<String> temp=new ArrayList<String>();
public A()
{
initialize();
}
public void initialize()
{
temp.add("abc");
temp.add("XYZ");
}
public ArrayList<String> getTemp()
{
return this.temp;
}
}
And Then As per Above Your Class B will Be
**B.java**
class B
{
private A a=null;
public B(A aa)
{
this.a = aa;
}
}
And Then you have to go for main method like
**Temp.java**
public class Temp {
public static void main(String... args)
{
A a = new A();
B b = new B(a);
System.out.println(a.getTemp().size());//output is 2
System.out.println(a.getTemp().get(0));//abc
}
}
I have the following class:
public class RefactorMe {
private static List<Event<Apple>> mAppleEventList = new ArrayList<Event<Apple>>();
private static List<Event<Banana>> mBananaEventList = new ArrayList<Event<Banana>>();
private static List<Event<Orange>> mOrangeEventList = new ArrayList<Event<Orange>>();
public static List<Event<Apple>> getAppleList() {
return mAppleEventList;
}
public static List<Event<Banana>> getBananaEventList() {
return mBananaEventList;
}
public static List<Event<Orange> getOrangeList() {
return mOrangeEventList;
}
public static void addAppleEvent(Event<Apple> pEvent) {
mAppleEventList.add(pEvent);
}
public static void addBananaEvent(Event<Banana> pEvent) {
mBananaEventList.add(pEvent);
}
public static void addOrangeEvent(Event<Orange> pEvent) {
mOrangeEventList.add(pEvent);
}
}
I tried refactoring it using the Visitor pattern but could not get it to work because of the generics.. Is there a better way to do this?
Following on #user902383 by using the Map here is a solution for you in Java 7:
public class RefactorMe {
class Event<K> {
public K getNewObject() {
return null;
}
}
private static Map<Class<?>, List<Event<?>>> eventLists = new HashMap<>();
public static <E> List<Event<E>> getEventList(Class<E> clazz) {
return (List) eventLists.get(clazz);
}
public static <E extends Event<E>> void addEvent(Event<E> pEvent) {
Class<E> key = (Class<E>) pEvent.getNewObject().getClass();
List<Event<?>> events = eventLists.get(key);
if (events == null) {
events = new ArrayList<>();
eventLists.put(key, events);
}
events.add(pEvent);
}
}