1. Overview
1.概述
In this tutorial, we’ll explore the process of creating Java objects using their class names. The Java Reflection API offers various methods for accomplishing this task. However, determining the most suitable one for the current context can be challenging.
在本教程中,我们将探讨使用类名创建 Java 对象的过程。Java Reflection API 提供了多种完成此任务的方法。但是,确定最适合当前上下文的方法可能具有挑战性。
To address this, let’s begin with a straightforward approach and gradually refine it to a more efficient solution.
为了解决这个问题,让我们从一个简单的方法开始,逐步完善成一个更有效的解决方案。
2. Create Objects Using Class Name
2.使用类名创建对象
Let’s picture a motor service center. This center handles maintenance and repairs for motor vehicles, using job cards to categorize and manage service requests. We can represent this as a class diagram:
让我们想象一个汽车服务中心。该中心处理机动车辆的维护和修理,使用工作卡对服务请求进行分类和管理。我们可以用类图来表示:
Let’s take a look at the MaintenanceJob and RepairJob classes:
让我们来看看 MaintenanceJob 和 RepairJob 类:
public class MaintenanceJob {
public String getJobType() {
return "Maintenance Job";
}
}
public class RepairJob {
public String getJobType() {
return "Repair Job";
}
}
Now, let’s implement the BronzeJobCard:
现在,让我们来实现 BronzeJobCard :
public class BronzeJobCard {
private Object jobType;
public void setJobType(String jobType) throws ClassNotFoundException,
NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class jobTypeClass = Class.forName(jobType);
this.jobType = jobTypeClass.getDeclaredConstructor().newInstance();
}
public String startJob() {
if(this.jobType instanceof RepairJob) {
return "Start Bronze " + ((RepairJob) this.jobType).getJobType();
}
if(this.jobType instanceof MaintenanceJob) {
return "Start Bronze " + ((MaintenanceJob) this.jobType).getJobType();
}
return "Bronze Job Failed";
}
}
In BronzeJobCard, Class.forName() takes the fully qualified name of the class to return the raw job object. Later, startJob() uses type-casting on the raw object to get the correct job type. On top of these disadvantages, there is also the overhead of handling the exceptions.
在 BronzeJobCard 中,Class.forName() 使用类的完全限定名称来返回原始的工作对象。随后,startJob() 会使用原始对象上的 type-casting 来获取正确的作业类型。除了这些缺点外,处理异常也是一项开销。
Let’s see it in action:
让我们看看它的实际效果:
@Test
public void givenBronzeJobCard_whenJobTypeRepairAndMaintenance_thenStartJob() throws ClassNotFoundException,
InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
BronzeJobCard bronzeJobCard1 = new BronzeJobCard();
bronzeJobCard1.setJobType("com.baeldung.reflection.createobject.basic.RepairJob");
assertEquals("Start Bronze Repair Job", bronzeJobCard1.startJob());
BronzeJobCard bronzeJobCard2 = new BronzeJobCard();
bronzeJobCard2.setJobType("com.baeldung.reflection.createobject.basic.MaintenanceJob");
assertEquals("Start Bronze Maintenance Job", bronzeJobCard2.startJob());
}
So, the above method started two Jobs, a repair job and a maintenance job.
因此,上述方法启动了两个工作,一个是维修工作,一个是维护工作。
A few months later, the service center decided to start a paint job as well. So, we created a new class PaintJob, but can BronzeJobCard accommodate this new addition? Let’s see:
几个月后,服务中心决定开始油漆工作。因此,我们创建了一个新类PaintJob,但BronzeJobCard能否容纳这个新增加的类呢?让我们来看看:
@Test
public void givenBronzeJobCard_whenJobTypePaint_thenFailJob() throws ClassNotFoundException,
InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
BronzeJobCard bronzeJobCard = new BronzeJobCard();
bronzeJobCard.setJobType("com.baeldung.reflection.createobject.basic.PaintJob");
assertEquals("Bronze Job Failed", bronzeJobCard.startJob());
}
That failed miserably! Due to the use of raw objects, BronzeJobCard is unable to handle the new PaintJob.
但这一次却惨遭失败!由于使用了原始对象,BronzeJobCard 无法处理新的 PaintJob 。
3. Create Objects Using the Raw Class Object
3.使用原始类对象创建对象
In this section, we’ll upgrade the Job card to create the jobs using java.lang.Class instead of the name of the class. First, take a look at the class diagram:
在本节中,我们将升级作业卡,以便使用 java.lang.Class 而不是类名来创建作业。首先,请看一下类图:
Let’s see how different SilverJobCard is from the BronzeJobCard:
让我们看看 SilverJobCard 和 BronzeJobCard 有什么不同:
public class SilverJobCard {
private Object jobType;
public void setJobType(Class jobTypeClass) throws
NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
this.jobType = jobTypeClass.getDeclaredConstructor().newInstance();
}
public String startJob() {
if (this.jobType instanceof RepairJob) {
return "Start Silver " + ((RepairJob) this.jobType).getJobType();
}
if (this.jobType instanceof MaintenanceJob) {
return "Start Silver " + ((MaintenanceJob) this.jobType).getJobType();
}
return "Silver Job Failed";
}
}
It no longer relies on the fully qualified name of the job classes to create the objects. However, the issue with raw objects and exceptions remains unchanged.
它不再依赖作业类的完全限定名称来创建对象。但是,原始对象和异常的问题仍未改变。
As shown below, it can also handle creating the jobs and then starting them:
如下图所示,它还可以处理创建工作和启动工作:
@Test
public void givenSilverJobCard_whenJobTypeRepairAndMaintenance_thenStartJob() throws InvocationTargetException,
NoSuchMethodException, InstantiationException, IllegalAccessException {
SilverJobCard silverJobCard1 = new SilverJobCard();
silverJobCard1.setJobType(RepairJob.class);
assertEquals("Start Silver Repair Job", silverJobCard1.startJob());
SilverJobCard silverJobCard2 = new SilverJobCard();
silverJobCard2.setJobType(MaintenanceJob.class);
assertEquals("Start Silver Maintenance Job", silverJobCard2.startJob());
}
But, like BronzeJobCard, SilverJobCard also fails to accommodate the new PaintJob:
但是,与 BronzeJobCard 一样,SilverJobCard 也无法容纳新的 PaintJob :
@Test
public void givenSilverJobCard_whenJobTypePaint_thenFailJob() throws ClassNotFoundException,
InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
SilverJobCard silverJobCard = new SilverJobCard();
silverJobCard.setJobType(PaintJob.class);
assertEquals("Silver Job Failed", silverJobCard.startJob());
}
Also, the method setJobType() does not restrict the passing of any object other than RepairJob and MaintenanceJob. This might result in erroneous code during the development stage.
此外,方法 setJobType() 并不限制传递除 RepairJob 和 MaintenanceJob 以外的任何对象。这可能会在开发阶段导致错误代码。
4. Create Objects Using Class Object and Generics
4.使用 Class 对象和泛型创建对象
Earlier, we saw how raw objects are affecting the quality of the code. In this section, we’ll address it. But first, take a look at the class diagram:
前面,我们看到了原始对象是如何影响代码质量的。在本节中,我们将讨论这个问题。首先,请看一下类图:
This time, we got rid of the raw objects. GoldJobCard takes the type parameter and makes use of Generics in the method setJobType(). Let’s check the implementation:
这一次,我们摆脱了原始对象。GoldJobCard接收类型参数,并在方法setJobType()中使用Generics。让我们检查一下实现:
public class GoldJobCard<T> {
private T jobType;
public void setJobType(Class<T> jobTypeClass) throws
NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
this.jobType = jobTypeClass.getDeclaredConstructor().newInstance();
}
public String startJob() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
return "Start Gold " + this.jobType.getClass().getMethod("getJobType", null)
.invoke(this.jobType).toString();
}
}
Interestingly, startJob() is now invoking the method on the object using the Reflection API. Finally, we also got rid of the need for type-casting. Let’s see how it behaves:
有趣的是,startJob() 现在使用 Reflection API 在对象上调用方法。最后,我们还摆脱了类型转换的需要。让我们看看它是如何运行的:
@Test
public void givenGoldJobCard_whenJobTypeRepairMaintenanceAndPaint_thenStartJob() throws InvocationTargetException,
NoSuchMethodException, InstantiationException, IllegalAccessException {
GoldJobCard<RepairJob> goldJobCard1 = new GoldJobCard();
goldJobCard1.setJobType(RepairJob.class);
assertEquals("Start Gold Repair Job", goldJobCard1.startJob());
GoldJobCard<MaintenanceJob> goldJobCard2 = new GoldJobCard();
goldJobCard2.setJobType(MaintenanceJob.class);
assertEquals("Start Gold Maintenance Job", goldJobCard2.startJob());
GoldJobCard<PaintJob> goldJobCard3 = new GoldJobCard();
goldJobCard3.setJobType(PaintJob.class);
assertEquals("Start Gold Paint Job", goldJobCard3.startJob());
}
Here, it handles the PaintJob as well.
在这里,它也会处理 PaintJob 。
But still, we’re not able to restrict the objects passed into the startJob() method during the development phase. As a result, it would fail for an object that does not have the getJobType() method like MaintenanceJob, RepairJob, and PaintJob.
但是,我们仍然无法在开发阶段限制传入 startJob() 方法的对象。因此,对于不具备 getJobType() 方法的对象,如 MaintenanceJob, RepairJob, 和 PaintJob, 它将会失败。
5. Create Objects Using Type Parameter Extends
5.使用类型参数扩展创建对象
It’s time to address the issue raised earlier. Let’s start with the customary class diagram:
是时候解决之前提出的问题了。让我们从习惯的类图开始:
We’ve introduced the Job interface, which all Job objects must implement. Furthermore, PlatinumJobCard now accepts only Job objects, indicated by the T extends Job parameter.
我们引入了Job接口,所有Job对象都必须实现该接口。此外,PlatinumJobCard 现在只接受 Job 对象,由 T extends Job 参数表示。
Actually, this approach closely resembles the Factory Design Pattern. We can introduce a JobCardFactory that can handle the creation of the Job objects.
实际上,这种方法与工厂设计模式非常相似。我们可以引入一个 JobCardFactory 来处理作业对象的创建。
Moving on, we can now look at the implementation:
接下来,我们可以看看执行情况:
public class PlatinumJobCard<T extends Job> {
private T jobType;
public void setJobType(Class<T> jobTypeClass) throws
NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
this.jobType = jobTypeClass.getDeclaredConstructor().newInstance();
}
public String startJob() {
return "Start Platinum " + this.jobType.getJobType();
}
}
We got rid of the Reflection API and the type-casting from the startJob() method by introducing the Job interface. Thankfully, now PlatinumJobCard will be able to handle future Job types without any modification to it. Let’s see it in action:
通过引入 Job 接口,我们摆脱了 Reflection API 和 startJob() 方法中的类型转换。值得庆幸的是,现在PlatinumJobCard将能够处理未来的Job类型,而无需对其进行任何修改。让我们来看看它的实际应用:
@Test
public void givenPlatinumJobCard_whenJobTypeRepairMaintenanceAndPaint_thenStartJob() throws InvocationTargetException,
NoSuchMethodException, InstantiationException, IllegalAccessException {
PlatinumJobCard<RepairJob> platinumJobCard1 = new PlatinumJobCard();
platinumJobCard1.setJobType(RepairJob.class);
assertEquals("Start Platinum Repair Job", platinumJobCard1.startJob());
PlatinumJobCard<MaintenanceJob> platinumJobCard2 = new PlatinumJobCard();
platinumJobCard2.setJobType(MaintenanceJob.class);
assertEquals("Start Platinum Maintenance Job", platinumJobCard2.startJob());
PlatinumJobCard<PaintJob> platinumJobCard3 = new PlatinumJobCard();
platinumJobCard3.setJobType(PaintJob.class);
assertEquals("Start Platinum Paint Job", platinumJobCard3.startJob());
}
6. Conclusion
6.结论
In this article, we explored the various ways to create objects with the class name and the Class object. We showed how related objects can implement a base interface. Then, it can be used further to streamline the object creation process. With this approach, there’s no need for type-casting, and also, it ensures the use of the Job interface, enforcing type-checking during development.
在本文中,我们探讨了使用类名和 Class 对象创建对象的各种方法。我们展示了相关对象如何实现基础接口。然后,可以进一步使用它来简化对象创建过程。有了这种方法,就不需要类型转换,而且还能确保使用 Job 接口,在开发过程中执行类型检查。
As usual, the code used in this article can be found over on GitHub.