提问人:Azn Stride 提问时间:3/16/2023 最后编辑:Azn Stride 更新时间:3/16/2023 访问量:359
如何使我的方法根据列表的输入类型返回不同类型的列表?
How do I make my method return different types of list base on the input type of list?
问:
我想为名为 的方法创建从输入到输出的映射,但以“智能”方式。处理不同类型列表的方式在本质上非常相似,因为输入列表共享相同的属性,但来自不同的类。List<T>
List<S>
foo(List<T>)
foo(List<T>)
我的目的是在返回输出之前,只需对输入类型进行少量检查,即可重用尽可能多的实现。foo()
一种方法是在foo
if (items.get(0) instanceOf Cat) {
List<Kitten> kittens = items.stream().map(cat -> Kitten.builder().name(cat.getName()).build().toList();
return kittens;
}
if (items.get(0) instanceOf Dog) {
List<Puppy> puppies = items.stream().map(dog -> Puppy.builder().name(dog.getName()).build().toList();
return puppies;
}
,但感觉不对,因为如果我添加另一种类型,我将不得不添加另一个if条件。Bird
我想,如果我想为不同类型的输入使用不同的返回类型,另一种实现此目的的方法是为特定类型的列表创建自定义类,即
class DogList {
private List<Dog> dogs;
}
class CatList {
private List<Cat> cats;
}
class KittenList {
private List<Kitten> kittens;
}
class PuppyList {
private List<Puppy> puppies;
}
// And creating method for each type that's sth like
public KittenList foo(CatList cats) {
List<Kitten> kittens = cats.getCats().stream().map(cat ->
Kitten.builder().name(cat.getName()).build().toList();
return kittens;
}
public PuppyList foo(DogList dogs) {
List<Puppy> puppies = dogs.getCats().stream().map(cat ->
Puppy.builder().name(dogs.getName()).build().toList();
return puppies;
}
但是这样做感觉很奇怪,因为我创建自定义类只是为了包装列表。我还复制了 99% 的 foo 实现。并且这里的实现几乎相同,所以我更愿意重用相同的方法。.
答:
List
type 仅包含 1 个类,例如 us 表示字符串列表。与您的示例相同,表示 T 的列表。List<String>
List<T>
我可以建议创建一个超类,然后向 和 添加继承。Animal
Dog
Cat
public class Animal {
Integer legCount;
Integer age;
}
public class Dog extends Animal {
String name;
public Dog(String name, int legCount, int age) {
super(legCount, age);
this.name = name;
}
}
public class Cat extends Animal {
String name;
public Cat(String name, int legCount, int age) {
super(legCount, age);
this.name = name;
}
}
将狗和猫变量添加到动物列表中
Dog dog1 = new Dog("Joni", 4, 2);
Cat cat1 = new Cat("Ron", 4, 3);
List<Animal> animals = new ArrayList();
animals.add(dog1);
animals.add(cat1);
然后在 foo 函数中变为
List<Puppy> puppies = new ArrayList();
List<Kitten> kittens = new ArrayList();
public void foo(List<Animal> animals) {
for(animal in animals) {
if(animals insntanceOf Dog) {
Puppy puppy = ...
puppies.add(puppy);
}
if(animals insntanceOf Kitten) {
Kitten kitten = ...
kittens.add(kitten);
}
}
}
为所有动物做一个循环
评论
首先,我们需要确保 , , 并且有一个共同的父类:Cat
Dog
Kitten
Puppy
Animal
class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Dog extends Animal {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Cat extends Animal {
private String name;
public Cat(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Kitten extends Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Kitten{" +
"name='" + name + '\'' +
'}';
}
}
class Puppy extends Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Puppy{" +
"name='" + name + '\'' +
'}';
}
}
然后使用像这样的通用方法:
public static <R extends Animal, T extends Animal> List<T> foo(List<R> source, Class<T> target) {
return source.stream().map(v -> {
T t = null;
try {
t = target.newInstance();
t.setName(v.getName());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return t;
}).collect(Collectors.toList());
}
指定两个参数:源列表和目标类型。List<R> source
Class<T> target
我们需要约束类型和类型,因为在方法内部,我们需要调用相应的 , 方法,如果是任何类型,那么我们无法确定它们有 , 方法,如何保证这一点?
很简单,如果 和 被要求是 的子类型,可以保证它们有 和 方法。因此,我们向泛型方法添加约束。T
R
setName
getName
setName
getName
T
R
Animal
setName
getName
<R extends Animal, T extends Animal>
在方法实现中,我们将源列表中的每个元素 (R) 转换为目标类型 (T)。
如果添加新类型,则只需添加相应的类型描述类即可。
以下是完整的测试代码,需要你自己了解和处理代码的细节:
public class StackOverflow {
public static void main(String[] args) {
List<Dog> dogs = new ArrayList<Dog>(){{
add(new Dog("dog 1"));
add(new Dog("dog 2"));
add(new Dog("dog 3"));
add(new Dog("dog 4"));
}};
List<Cat> cats = new ArrayList<Cat>(){{
add(new Cat("cat 1"));
add(new Cat("cat 2"));
add(new Cat("cat 3"));
}};
DogList dogList = new DogList();
dogList.setDogs(dogs);
CatList catList = new CatList();
catList.setCats(cats);
List<Puppy> puppies = foo(dogList.getDogs(), Puppy.class);
System.out.println(puppies);
List<Kitten> kittens = foo(catList.getCats(), Kitten.class);
System.out.println(kittens);
}
public static <R extends Animal, T extends Animal> List<T> foo(List<R> source, Class<T> target) {
return source.stream().map(v -> {
T t = null;
try {
t = target.newInstance();
t.setName(v.getName());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return t;
}).collect(Collectors.toList());
}
}
class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Dog extends Animal {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Cat extends Animal {
private String name;
public Cat(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Kitten extends Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Kitten{" +
"name='" + name + '\'' +
'}';
}
}
class Puppy extends Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Puppy{" +
"name='" + name + '\'' +
'}';
}
}
class DogList {
private List<Dog> dogs;
public List<Dog> getDogs() {
return dogs;
}
public void setDogs(List<Dog> dogs) {
this.dogs = dogs;
}
}
class CatList {
public List<Cat> getCats() {
return cats;
}
public void setCats(List<Cat> cats) {
this.cats = cats;
}
private List<Cat> cats;
}
这是做类似事情的一种方法 - 以一种类型安全的方式将特定动物的列表转换为其幼崽的列表。
它要求您在声明类时将幼崽动物的类型指定为类型参数:
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Dog> dogs = List.of(new Dog(), new Dog());
List<Cat> cats = List.of(new Cat(), new Cat());
List<Puppy> puppies = createYoungList(dogs);
List<Kitten> kittens = createYoungList(cats);
//List<Puppy> kittens = createYoung(cats); compilation error
}
static <P extends Animal<Y>,Y> List<Y> createYoungList(List<P> parents) {
return parents.stream().map(Animal::createYoung).toList();
}
}
interface Animal<Y> {
Y createYoung();
}
class Puppy {}
class Kitten {}
class Dog implements Animal<Puppy> {
@Override
public Puppy createYoung() {
return new Puppy();
}
}
class Cat implements Animal<Kitten> {
@Override
public Kitten createYoung() {
return new Kitten();
}
}
添加 的新实现不需要对 进行任何更改。Animal
createYoungList
我试图复制你的问题。这是一种可能的解决方案:
创建一个超类并声明类之间的映射:Animal
private Map<Class<Animal>, Class<Animal>> classMap = Map.ofEntries(
new AbstractMap.SimpleEntry(Cat.class, Kitten.class),
new AbstractMap.SimpleEntry(Dog.class, Puppy.class)
);
遍历映射条目以处理 :foo
for(Entry<Class<Animal>, Class<Animal>> entry : classMap.entrySet()){
if (items.get(0).getClass() == entry.getKey()) {
List list = items.stream().map(item -> {
Animal animal = new Animal();
try {
// instantiate animal young
animal = entry.getValue().getConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
animal.setName(item.getName());
// mapping values ...
return animal;
}).collect(Collectors.toList());
return list;
}
}
每当您需要添加更多动物类时,只需将它们添加到类映射中即可:
private Map<Class<Animal>, Class<Animal>> classMap = Map.ofEntries(
new AbstractMap.SimpleEntry(Cat.class, Kitten.class),
new AbstractMap.SimpleEntry(Dog.class, Puppy.class),
new AbstractMap.SimpleEntry(Cow.class, Calf.class)
);
希望对您有所帮助。
我有一个想法:首先通过源类注册映射器函数。对于新的源类,只需注册一个新的映射器函数。 一个缺点是:有一个显式的类转换来获取目标列表。我不知道如何删除它。
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Dog> dogs = Arrays.asList(new Dog("d1"), new Dog("d2"));
Map<Class, Function> register = new HashMap();
register.put(Dog.class, (Function<Dog, Puppy>) dog -> new Puppy(dog.getName()));
List<Puppy> puppies = (List<Puppy>) dogs.stream().map(register.get(Dog.class)).collect(Collectors.toList());
System.out.println("puppies:" + puppies);
}
}
class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Puppy {
private String name;
public Puppy(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Puppy{" +
"name='" + name + '\'' +
'}';
}
}
我不确定你的意图是什么,但希望这能帮助你实现目标。
从类型化方法返回类型化列表的一种方法是传递类型标记:
private List<Object> items;
public <T> List<T> getAll(Class<T> clazz) {
List<T> result = new ArrayList<>();
for (Object obj : items) {
if (clazz.isInstance(obj)) {
result.add((T)obj);
}
}
return result;
}
调用时推断类型:
List<Dog> dogs = myClass.getAll(Dog.class);
评论
List<Object> animals = items.stream().map(animal -> Animal.builder().name(animal.getName()).build().toList();