Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method to accept one class object or another class object

Tags:

java

I have two methods like the following

private List<Long> getIds(String name, List<Cat> cats) {
    List<Long> catIds = new ArrayList<>();
    for (Cat cat : cats) {
        if (cat.getName().equals(name)) catIds.add(cat.getId());
    }
    return catIds;
}

private List<Long> getIds(String name, List<Dog> dogs) {
    List<Long> dogIds = new ArrayList<>();
    for (Dog dog : dogs) {
        if (dog.getName().equals(name)) dogIds.add(dog.getId());
    }
    return dogIds;
}

My cat and dog class are as follows

public class Cat {
    String name;
    Long id;
    // additional variables
    // getters and setters
}

public class Dog {
    String name;
    Long id;
    // additional variables
    // getters and setters
}

Just to avoid redundancy, I wanted to convert the above two methods to a single generic method.

I tried the following

private List<Long> getIds(String name, List<T> objects) {
    List<Long> ids = new ArrayList<>();
    for (T object : objects) {
        if (object.getName().equals(name)) ids.add(object.getId());
    }
    return ids;
}

But it does not work out as it complains that the generic T does not have getName or getId

Here Cat and Dog are in-built java classes. As a result I CANNOT perform inheritance and provide a super class Animal for them with name and id as data variables.

Is there any way I could accomplish merging the two above methods without implementing inheritance?

like image 921
Philip John Avatar asked Mar 16 '16 09:03

Philip John


People also ask

What is the method that can allow an object to be accessed by another class?

In java, if a method of a class is declared with the “protected” keyword, then it can be accessed by any other class of the same package. A method declared with the protected keyword can't be accessed out of the package directly. However, it can be accessed outside the package with the help of inheritance.

Which of these method of object class is used to obtain a class of an object at run time?

Object. getClass() If an instance of an object is available, then the simplest way to get its Class is to invoke Object. getClass() .


4 Answers

Another possibility: If you are using Java 8, you could pass in the getter methods for name and ID as additional parameters into the method. This is type-safe and does neither need reflection nor modifications to the original classes.

private <T> List<Long> getIds(String name, List<T> objects, Function<T, String> getName, Function<T, Long> getId) {
    List<Long> ids = new ArrayList<>();
    for (T object : objects) {
        if (getName.apply(object).equals(name)) {
            ids.add(getId.apply(object));
        }
    }
    return ids;
}

Or shorter, using streams:

private <T> List<Long> getIds(String name, List<T> objects, Function<T, String> getName, Function<T, Long> getId) {
    return objects.stream().filter(o -> getName.apply(o).equals(name))
            .map(getId).collect(Collectors.toList());
}

Usage:

List<Long> ids = getIds("some name", listOfCats, Cat::getName, Cat::getId);

Of course, while this way the body of the method is the same for all the cases, you have to call it in different ways, depending on the input. For this, you could provide an entry method, calling the other method in the appropriate way or raising an Exception in case the type is wrong.

private List<Long> getIds(String name, List<?> objects) {
    if (objects.size() == 0) {
        return Collections.emptyList();
    }
    if (objects.get(0) instanceof Cat) {
        return getIds(name, (List<Cat>) objects, Cat::getName, Cat::getId);
    }
    if (objects.get(0) instanceof Dog) {
        ...
    }
    throw new IllegalArgumentException("List containing unsupported type!");
}
like image 64
tobias_k Avatar answered Nov 02 '22 06:11

tobias_k


Ok, I've got that you cannot use any type of inheritance. Here is a short solution with Reflection API then:

private static List<Long> getIds(String name, List<?> objects) {
    List<Long> ids = new ArrayList<>();
    for (Object object : objects) {
        try {
            Method getName = null;
            Method getId = null;

            for (Method method : object.getClass().getMethods()) {
                if (method.getName().equals("getName") && method.getReturnType().equals(String.class) && method.getParameterTypes().length == 0) {
                    getName = method;
                }
                if (method.getName().equals("getId") && method.getReturnType().equals(Long.class) && method.getParameterTypes().length == 0) {
                    getId = method;
                }
                if (getName != null && getId != null && getName.invoke(object).equals(name)) {
                   ids.add((Long) getId.invoke(object));
                   break;
                }
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    }
    return ids;
}
like image 44
Cootri Avatar answered Nov 02 '22 04:11

Cootri


You need to define a common interface for Cat and Dog, for example Animal.

Than you can pass a List<Animal> as parameter.

like image 42
Davide Lorenzo MARINO Avatar answered Nov 02 '22 06:11

Davide Lorenzo MARINO


Can you just make an interface for both Dog and Cat?

interface animal {
    ...
    Long getId();
}

public class Cat implements Animal
public class Dog implements Animal

then your method would become

private List<Long> getIds(String name, List<Animal> animals) {
    List<Long> ids = new ArrayList<>();
    for (Animal animal : animals) {
        ids.add(animal.getId());
    }
    return ids;
}

Regarding the in-built class you've mentioned, you can wrap them in another class and implements the interface(or superclass) describing the desired behavior.

I once had to provide same interface for log4j and logback event Object, they two are quite different definitely.

here the wrapper goes..

public abstract class EventWrapper {
    public abstract String getFormattedMessage();
}

And the child class which actually holds the in-built class

public class Log4jEvent extends EventWrapper {
    LoggingEvent event;

    public Log4jEvent(LoggingEvent event) {
        this.event = event;
    }

    @Override
    public String getFormattedMessage() {
        if (event.getMessage() != null) {
            return event.getMessage().toString();
        } else {
            return null;
        }
    }
}

LogbackEvent class is very similar to Log4jEvent class, and in other parts of the code, there are many method which receives argument type of EventWrapper. Maybe this case would help you develop your solution.

like image 37
Adrian Seungjin Lee Avatar answered Nov 02 '22 06:11

Adrian Seungjin Lee