如果我有两个子类 Employee
和 Intern
,它们都扩展了类 Person
。假设我有 List<Person> contacts
是一个列表,其中包含在公司工作的每个人,Employee
和 Intern
现在我想写一个函数findPerson(String name)
。这个函数假设通过在 contacts
中搜索他们的名字来返回这个人,而不知道他们是属于 Employee
还是 Intern
。但是,如果找到该对象,我想返回该对象。
但是,我认为 Java 不能为函数返回动态类型。除了编写两个函数之外,还有什么我可以做的吗?
我试图让函数返回对象类型 Person
。但是,这样我无法将它们转换为适当的类型,尤其是我无法知道它们是 Employee
还是 Intern
答案 0 :(得分:2)
Contacts.findPerson 方法可以有一个通用的返回类型,例如:
<T extends Person > T findPerson(String name)
这将为呼叫者提供他们正在寻找的特定类型的人员(我认为您的意思是人员,而不是公司,对吗?)。为了确保返回的类型 T 匹配特定的类,您可以说:
<T extends Person > T findPerson(Class<T> type, name)
现在类型 T 将绑定到与传入的类相同的类型,因此用法如下:
var intern = contacts.findPerson(Intern.class, "Elon Musk");
答案 1 :(得分:1)
好吧,您需要两个函数 - findEmployee
和 findIntern
,并分别进行 instanceof
检查:
Optional<Employee> findEmployee(List<Company> list, String name) {
for (Company c : list) {
if (c instanceof Employee && c.getName().equals(name)) {
return Optional.of((Employee) c);
}
}
return Optional.empty();
}
但我会考虑如何修改 Company
类,因此不需要将其强制转换为特定的子类(使用多态)。
答案 2 :(得分:1)
我有两个子类 Employee 和 Intern 扩展了 Company 类
作为您mentioned in a Comment,这种命名没有意义。因此,让我们将其更改为 Person
作为父类,具有两个子类,Employee
和 Intern
。我们会忘记Company
。
如果在编译时知道所有可能的子类,那么我们应该使用在 Java 16 中预览的新 sealed classes 特性。
我们的超类,Person
。
package work.basil.example.extending;
import java.time.*;
import java.util.Objects;
import java.util.UUID;
public abstract sealed class Person
permits Employee, Intern
{
// Member fields
UUID id;
String name;
LocalDate whenHired;
// Constructors
public Person ( final UUID id , final String name , final LocalDate whenHired )
{
this.id = Objects.requireNonNull( id );
this.name = Objects.requireNonNull( name );
this.whenHired = Objects.requireNonNull( whenHired );
}
// Logic
Period getPeriodOfEmployment ( )
{
ZoneId z = ZoneId.systemDefault();
LocalDate today = LocalDate.now( z );
Period p = Period.between( this.whenHired , today );
return p;
}
// Object overrides.
@Override
public String toString ( )
{
return "Person{ " +
"id=" + id +
" | name='" + name + '\'' +
" | whenHired=" + whenHired +
" }";
}
}
两个具体的子类,Employee
和 Intern
。
package work.basil.example.extending;
import java.time.LocalDate;
import java.util.UUID;
public final class Employee extends Person
{
public Employee ( UUID id , String name , LocalDate whenHired )
{
super( id , name , whenHired );
}
}
package work.basil.example.extending;
import java.time.LocalDate;
import java.util.UUID;
public final class Intern extends Person
{
public Intern ( UUID id , String name , LocalDate whenHired )
{
super( id , name , whenHired );
}
}
Person
个对象你问:
<块引用>写一个函数 findPerson(String name) ... 事先不知道他们是属于 Employee 还是 Intern
Employee
和 Intern
都从它们的超类 name
继承了一个 Person
成员字段。所以我们不关心哪个子类型。只需询问名称并与所需名称进行比较。
当然,我们可能会发现带有该名称的零个、一个或多个 Person
对象。所以我们应该返回一个 List
或 Set
找到的对象。对于 Set
,我们需要 equals
和 hashCode
的实现。我们还没有写这些,所以让我们暂时坚持使用 List
。
List < Person > fetchPeopleByName ( final String name , final List < Person > people )
{
Objects.requireNonNull( people );
List < Person > peopleSharingName = new ArrayList <>( people.size() );
for ( Person person : people )
{
if ( person.name.equalsIgnoreCase( name ) )
{
peopleSharingName.add( person );
}
}
return List.copyOf( peopleSharingName ); // Return unmodifiable list.
}
执行该方法的代码。
List < Person > people = List.of(
new Employee( UUID.fromString( "a4dcf239-305f-40f1-b6cc-32e3e6643451" ) , "Alice" , LocalDate.of( 2008 , Month.JANUARY , 23 ) ) ,
new Intern( UUID.fromString( "e5ac2a48-f25a-42e1-af58-6ce597cc93ed" ) , "Bob" , LocalDate.of( 2020 , Month.DECEMBER , 7 ) ) ,
new Intern( UUID.fromString( "2c24630b-b7e2-4e9f-87ab-7c996137894f" ) , "Carol" , LocalDate.of( 2021 , Month.JANUARY , 11 ) ) ,
new Employee( UUID.fromString( "4b1f918d-c95d-44c5-a85d-d092e3b83c51" ) , "Demetri" , LocalDate.of( 2011 , Month.MARCH , 16 ) )
);
List < Person > bobs = this.fetchPeopleByName( "Bob" , people );
System.out.println( "bobs = " + bobs );
运行时,我们找到一个名称与“Bob”匹配的 Person
对象。
bobs = [Person{ id=e5ac2a48-f25a-42e1-af58-6ce597cc93ed | name='Bob' | whenHired=2020-12-07 }]
你提到了你自己对最后一种方法的尝试:
<块引用>我试图让函数返回对象类型 Company Person。
是的,我们可以返回 Person
对象,因为我们通过 Person
超类型上声明的成员字段搜索 Person
对象。所以我们不关心子类型,Employee
或 Intern
。
您还谈到了您对这种方法的尝试:
<块引用>但是,这样我无法将它们转换为适当的类型,尤其是我无法知道它们是 Employee 还是 Intern
无需铸造。当询问他们的名字时,超类 Person
的成员字段,子类 Employee
或 Intern
的任何对象都将通过提供他们的名字来响应。因此,成为 Employee
或 Intern
与获得他们的名字无关。
Person
查找 Employee
对象您可能在其他情况下确实关心他们的子类型。例如,假设我们需要向每位员工发送有关其就业福利计划的信息。实习生没有这样的福利。因此,我们只想发送到恰好属于 Person
子类的 Employee
对象的子集。这是一些代码。
让我们创建一个方法来遍历 Person
对象列表,以仅返回子类 Employee
的对象。
我们使用 instanceOf
测试具体类。在较旧的 Java 中,我们必须在通过 instanceOf
测试后进行转换:( Employee ) person
。在最新的 Java 版本中,新语法使转换自动和隐式。
List < Employee > fetchEmployees ( final List < Person > people )
{
Objects.requireNonNull( people );
List < Employee > employees = new ArrayList <>( people.size() );
for ( Person person : people )
{
if ( person instanceof Employee employee ) // New syntax eliminates need to cast explicitly.
{
employees.add( employee );
}
}
return List.copyOf( employees );
}
执行该方法的代码。
List < Person > people = List.of(
new Employee( UUID.fromString( "a4dcf239-305f-40f1-b6cc-32e3e6643451" ) , "Alice" , LocalDate.of( 2008 , Month.JANUARY , 23 ) ) ,
new Intern( UUID.fromString( "e5ac2a48-f25a-42e1-af58-6ce597cc93ed" ) , "Bob" , LocalDate.of( 2020 , Month.DECEMBER , 7 ) ) ,
new Intern( UUID.fromString( "2c24630b-b7e2-4e9f-87ab-7c996137894f" ) , "Carol" , LocalDate.of( 2021 , Month.JANUARY , 11 ) ) ,
new Employee( UUID.fromString( "4b1f918d-c95d-44c5-a85d-d092e3b83c51" ) , "Demetri" , LocalDate.of( 2011 , Month.MARCH , 16 ) )
);
List < Employee > emps = this.fetchEmployees( people );
System.out.println( "emps = " + emps );
运行时,我们找到了两名员工。一个名为“Alice”,另一个名为“Demetri”。
emps = [Person{ id=a4dcf239-305f-40f1-b6cc-32e3e6643451 | name='Alice' | whenHired=2008-01-23 }, Person{ id=4b1f918d-c95d-44c5-a85d-d092e3b83c51 | name='Demetri' | whenHired=2011-03-16 }]
Employee
和我们可以结合两种方法来生成具有特定名称的 Employee
对象列表。
List < Person > people = List.of(
new Employee( UUID.fromString( "a4dcf239-305f-40f1-b6cc-32e3e6643451" ) , "Alice" , LocalDate.of( 2008 , Month.JANUARY , 23 ) ) ,
new Intern( UUID.fromString( "e5ac2a48-f25a-42e1-af58-6ce597cc93ed" ) , "Bob" , LocalDate.of( 2020 , Month.DECEMBER , 7 ) ) ,
new Intern( UUID.fromString( "2c24630b-b7e2-4e9f-87ab-7c996137894f" ) , "Carol" , LocalDate.of( 2021 , Month.JANUARY , 11 ) ) ,
new Employee( UUID.fromString( "4b1f918d-c95d-44c5-a85d-d092e3b83c51" ) , "Demetri" , LocalDate.of( 2011 , Month.MARCH , 16 ) )
);
// Empty list. Our only "Bob" is an intern, not an employee.
List < Employee > employeesNamedBob = this.fetchEmployees( this.fetchPeopleByName( "Bob" , people ) );
// A list of one `Employee` object named "Demetri".
List < Employee > employeesNamedDemetri = this.fetchEmployees( this.fetchPeopleByName( "Demetri" , people ) );