UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

211 lines (207 loc) 27.7 kB
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><head><title>Handcoding with Beans</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Handcoding with Beans"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-22-SECT-10"/>Handcoding with Beans</h1></div></div></div><p>So far, we’ve seen how to create and use beans within a bean application builder environment. That is the primary motivation for JavaBeans, at least in GUI development. But beans are not limited to being used by automated tools. There’s no reason we can’t use beans in handwritten code. You could use a builder to assemble beans for the user interface of your application and then load that serialized bean or a collection of beans in your own code, just as NetBeans does when told to use object serialization. We’ll give an example of that in a moment.</p><div class="sect2" title="Bean Instantiation and Type Management"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-22-SECT-10.1"/>Bean Instantiation and Type Management</h2></div></div></div><p><a id="I_indexterm22_id823548" class="indexterm"/>Beans are an abstraction over simple Java classes. They add, by convention, features that are not part of the Java language. To enable certain additional capabilities of JavaBeans, we use special tools that take the place of basic language operations. Specifically, when working with beans, we are provided with replacements for three basic Java operations: creating an object with <a id="I_indexterm22_id823563" class="indexterm"/><code class="literal">new</code>, checking the type of an object with the <a id="I_indexterm22_id823576" class="indexterm"/><code class="literal">instanceof</code> operator, and casting a type with a <code class="literal">cast</code> expression. In place of these, use the corresponding static methods of the <code class="literal">java.beans.Beans</code> class, shown in <a class="xref" href="ch22s10.html#learnjava3-CHP-22-TABLE-1" title="Table 22-1. Methods of the java.beans.Beans class">Table 22-1</a>.</p><div class="table"><a id="learnjava3-CHP-22-TABLE-1"/><p class="title">Table 22-1. Methods of the java.beans.Beans class</p><div class="table-contents"><table summary="Methods of the java.beans.Beans class" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col/><col/></colgroup><thead><tr><th style="text-align: left"><p>Operator</p></th><th style="text-align: left"><p>Equivalent</p></th></tr></thead><tbody><tr><td style="text-align: left"><p> <code class="literal">New</code> </p></td><td style="text-align: left"><p> <code class="literal">Beans.instantiate(classloader, name)</code> </p></td></tr><tr><td style="text-align: left"><p> <code class="literal">Instanceof</code> </p></td><td style="text-align: left"><p> <code class="literal">Beans.isInstanceOf(object, class)</code> </p></td></tr></tbody></table></div></div><p><code class="literal">Beans.instantiate()</code> is the <code class="literal">new</code> operation for beans. It takes a class loader and the name of a bean class or serialized bean as arguments. Its advantage over the plain <code class="literal">new</code> operator is that it can also load beans from a serialized form. If you use <a id="I_indexterm22_id823708" class="indexterm"/><code class="literal">instantiate()</code>, you don’t have to specify in advance whether you will provide the bean as a class or as a serialized object. The <code class="literal">instantiate()</code> method first tries to load a resource file based on the name bean, by turning package-style names (with dots) into a path-style name with slashes and then appending the suffix <span class="emphasis"><em>.ser</em></span>. For example, <code class="literal">magicbeans.NumericField</code> becomes <span class="emphasis"><em>magicbeans/NumericField.ser</em></span>. If the serialized form of the bean is not found, the <code class="literal">instantiate()</code> method attempts to create an instance of the class by name.<sup>[<a id="learnjava3-CHP-22-FN-3" href="#ftn.learnjava3-CHP-22-FN-3" class="footnote">47</a>]</sup></p><p><a id="I_indexterm22_id823770" class="indexterm"/> <code class="literal">Beans.isInstanceOf()</code> and <a id="I_indexterm22_id823782" class="indexterm"/><code class="literal">Beans.getInstanceOf()</code> do the jobs of checking a bean’s type and casting it to a new type. These methods were intended to allow one or more beans to work together to implement “virtual” or dynamic types. They are supposed to allow beans to take control of this behavior, providing different “views” of themselves. However, they currently don’t add any functionality and aren’t widely used.</p></div><div class="sect2" title="Working with Serialized Beans"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-22-SECT-10.2"/>Working with Serialized Beans</h2></div></div></div><p><a id="idx11158" class="indexterm"/>Remember the <code class="literal">Juggler</code> we serialized a while back? Well, it’s time to revive him, just like Han Solo from his “Carbonite” tomb in <span class="emphasis"><em>Star Wars</em></span>. We’ll assume that you saved the <code class="literal">Juggler</code> by flipping on the Serialization property while working with the <code class="literal">LearnJava1</code> class and that NetBeans, therefore, saved him in the file <span class="emphasis"><em>LearnJava1_juggler1.ser</em></span>. If you didn’t do this, you can use the following snippet of code to serialize the bean to a file of your choice:</p><a id="I_22_tt1237"/><pre class="programlisting"> <code class="c1">// Serialize a Juggler instance to a file...</code> <code class="kn">import</code> <code class="nn">magicbeans.sunw.demo.juggler.Juggler</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.io.*</code><code class="o">;</code> <code class="err"> </code> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">SerializeJuggler</code> <code class="o">{</code> <code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code> <code class="n">String</code> <code class="o">[]</code> <code class="n">args</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">Exception</code> <code class="o">{</code> <code class="n">Juggler</code> <code class="n">duke</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Juggler</code><code class="o">();</code> <code class="n">ObjectOutputStream</code> <code class="n">oout</code> <code class="o">=</code> <code class="k">new</code> <code class="n">ObjectOutputStream</code><code class="o">(</code> <code class="k">new</code> <code class="nf">FileOutputStream</code><code class="o">(</code><code class="s">"juggler.ser"</code><code class="o">)</code> <code class="o">);</code> <code class="n">oout</code><code class="o">.</code><code class="na">writeObject</code><code class="o">(</code> <code class="n">duke</code> <code class="o">);</code> <code class="n">oout</code><code class="o">.</code><code class="na">close</code><code class="o">();</code> <code class="o">}</code> <code class="o">}</code></pre><p>Once you have the frozen <span class="emphasis"><em>Juggler</em></span>, compile the following small application:</p><a id="I_22_tt1238"/><pre class="programlisting"> <code class="c1">//file: BackFromTheDead.java</code> <code class="kn">import</code> <code class="nn">java.awt.Component</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.beans.*</code><code class="o">;</code> <code class="err"> </code> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">BackFromTheDead</code> <code class="kd">extends</code> <code class="n">JFrame</code> <code class="o">{</code> <code class="err"> </code> <code class="kd">public</code> <code class="nf">BackFromTheDead</code><code class="o">(</code> <code class="n">String</code> <code class="n">name</code> <code class="o">)</code> <code class="o">{</code> <code class="kd">super</code><code class="o">(</code><code class="s">"Revived Beans!"</code><code class="o">);</code> <code class="k">try</code> <code class="o">{</code> <code class="n">Object</code> <code class="n">bean</code> <code class="o">=</code> <code class="n">Beans</code><code class="o">.</code><code class="na">instantiate</code><code class="o">(</code> <code class="n">getClass</code><code class="o">().</code><code class="na">getClassLoader</code><code class="o">(),</code> <code class="n">name</code> <code class="o">);</code> <code class="err"> </code> <code class="k">if</code> <code class="o">(</code> <code class="n">Beans</code><code class="o">.</code><code class="na">isInstanceOf</code><code class="o">(</code> <code class="n">bean</code><code class="o">,</code> <code class="n">JComponent</code><code class="o">.</code><code class="na">class</code><code class="o">)</code> <code class="o">)</code> <code class="o">{</code> <code class="n">JComponent</code> <code class="n">comp</code> <code class="o">=</code> <code class="o">(</code><code class="n">JComponent</code><code class="o">)</code> <code class="n">Beans</code><code class="o">.</code><code class="na">getInstanceOf</code><code class="o">(</code><code class="n">bean</code><code class="o">,</code> <code class="n">JComponent</code><code class="o">.</code><code class="na">class</code><code class="o">);</code> <code class="n">getContentPane</code><code class="o">().</code><code class="na">add</code><code class="o">(</code><code class="s">"Center"</code><code class="o">,</code> <code class="n">comp</code><code class="o">);</code> <code class="o">}</code> <code class="k">else</code> <code class="o">{</code> <code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code><code class="s">"Bean is not a JComponent..."</code><code class="o">);</code> <code class="o">}</code> <code class="o">}</code> <code class="k">catch</code> <code class="o">(</code> <code class="n">java</code><code class="o">.</code><code class="na">io</code><code class="o">.</code><code class="na">IOException</code> <code class="n">e1</code> <code class="o">)</code> <code class="o">{</code> <code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code><code class="s">"Error loading the serialized object"</code><code class="o">);</code> <code class="o">}</code> <code class="k">catch</code> <code class="o">(</code> <code class="n">ClassNotFoundException</code> <code class="n">e2</code> <code class="o">)</code> <code class="o">{</code> <code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code> <code class="s">"Can't find the class that goes with the object"</code><code class="o">);</code> <code class="o">}</code> <code class="o">}</code> <code class="err"> </code> <code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code> <code class="o">[]</code> <code class="n">args</code><code class="o">)</code> <code class="o">{</code> <code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">BackFromTheDead</code><code class="o">(</code> <code class="n">args</code><code class="o">[</code><code class="mi">0</code><code class="o">]</code> <code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">pack</code><code class="o">();</code> <code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code><code class="kc">true</code><code class="o">);</code> <code class="o">}</code> <code class="o">}</code></pre><p>Run this program, passing the name of your serialized object as an argument and making sure that our <span class="emphasis"><em>magicbeans.jar</em></span> file is in your classpath. The name should <span class="emphasis"><em>not</em></span> include the <span class="emphasis"><em>.ser</em></span> extension in the name; the <code class="literal">Beans.instantiate()</code> method adds this automatically in its search for the serialized or class version. The juggler should spring back to life, juggling once again as shown in <a class="xref" href="ch22s10.html#learnjava3-CHP-22-FIG-11" title="Figure 22-9. The restored Juggler">Figure 22-9</a>.</p><div class="figure"><a id="learnjava3-CHP-22-FIG-11"/><div class="figure-contents"><div class="mediaobject"><a id="I_22_tt1239"/><img src="httpatomoreillycomsourceoreillyimages1707713.png" alt="The restored Juggler"/></div></div><p class="title">Figure 22-9. The restored Juggler</p></div><p>In <code class="literal">BackFromTheDead</code>, we use <code class="literal">Beans.instantiate()</code> to load our serialized bean by name. We then check to see whether it is a GUI component using <code class="literal">Beans.isInstanceOf()</code>. (It is, because the <code class="literal">Juggler</code> is a subclass of <code class="literal">java.awt.Component</code>.) Finally, we cast the instantiated object to a <code class="literal">Component</code> with <code class="literal">Beans.getInstanceOf()</code> and add it to our application’s <code class="literal">JFrame</code>. Notice that we still need a static Java cast to turn the <code class="literal">Object</code> returned by <code class="literal">getInstanceOf()</code> into a <code class="literal">JComponent</code>. This cast may seem gratuitous, but it is the bridge between the dynamic beans lookup of the type and the static, compile-time view of the type.</p><p>Everything we’ve done here could be done using the plain <code class="literal">java.io.ObjectInputStream</code> discussed in <a class="xref" href="ch12.html" title="Chapter 12. Input/Output Facilities">Chapter 12</a>. But these bean management methods are intended to shield the user from details of how the beans are implemented and stored.</p><p>One more thing before we move on. We blithely noted that when the <code class="literal">Juggler</code> was restored, the bean began juggling again. This implies that threads were started when the bean was deserialized. Serialization doesn’t automatically manage transient resources such as threads or even loaded images, but it’s easy to take control of the process to finish reconstructing the bean’s state when it is deserialized. Have a look at the <code class="literal">Juggler</code> source code (provided with the examples) and refer to <a class="xref" href="ch12.html" title="Chapter 12. Input/Output Facilities">Chapter 12</a> for a discussion of object deserialization using the <code class="literal">readObject()</code> method.<a id="I_indexterm22_id824055" class="indexterm"/></p></div><div class="sect2" title="Runtime Event Hookups with Reflection"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-22-SECT-10.3"/>Runtime Event Hookups with Reflection</h2></div></div></div><p><a id="idx11157" class="indexterm"/>We’ve discussed reflection largely in terms of how design tools use it to analyze classes. Today, reflection is frequently finding its way into applications to perform dynamic activities that wouldn’t be possible otherwise. In this section, we’ll look at a dynamic event adapter that can be configured at runtime.</p><p>In <a class="xref" href="ch16.html" title="Chapter 16. Swing">Chapter 16</a>, we saw how adapter classes could be built to connect event firings to arbitrary methods in our code, allowing us to cleanly separate GUI and logic in our applications. In this chapter, we have seen how NetBeans interposes this adapter code between beans to do this for us.</p><p>The AWT/Swing event model reduces the need to subclass components to perform simple hookups. If we start relying heavily on special adapter classes, we can quickly end up with as many adapters as components. Anonymous inner classes let us hide these classes, but they’re still there. A potential solution for large or specialized applications is to create <span class="emphasis"><em>generic</em></span> event adapters that serve a number of event sources and targets simultaneously.</p><p>The <a id="I_indexterm22_id824113" class="indexterm"/><code class="literal">java.beans.EventHandler</code> is a dynamic event dispatcher that simply calls methods in response to events. What makes the <code class="literal">EventHandler</code> unusual in Java is that it is the first standard utility to use reflection to allow us to specify the method by <span class="emphasis"><em>name</em></span>. In other words, you ask the <code class="literal">EventHandler</code> to direct events to a handler by specifying the handler object and the string name of the method to invoke on that object.</p><p>We can use the <code class="literal">create()</code> method of <code class="literal">EventHandler</code> to get an adapter for a specified type of event listener, specifying a target object and method name to call when that event occurs. The target object doesn’t have to be a listener for the particular event type or any other particular kind of object. The following application, <code class="literal">DynamicHookup</code>, uses the <code class="literal">EventHandler</code> to connect a button to a <code class="literal">launchTheMissiles()</code> method in our class:</p><a id="I_22_tt1240"/><pre class="programlisting"> <code class="c1">//file: DynamicHookup.java</code> <code class="kn">import</code> <code class="nn">javax.swing.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.awt.event.*</code><code class="o">;</code> <code class="kn">import</code> <code class="nn">java.beans.EventHandler</code><code class="o">;</code> <code class="kd">public</code> <code class="kd">class</code> <code class="nc">DynamicHookup</code> <code class="kd">extends</code> <code class="n">JFrame</code> <code class="o">{</code> <code class="n">JLabel</code> <code class="n">label</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JLabel</code><code class="o">(</code> <code class="s">"Ready..."</code><code class="o">,</code> <code class="n">JLabel</code><code class="o">.</code><code class="na">CENTER</code> <code class="o">);</code> <code class="kt">int</code> <code class="n">count</code><code class="o">;</code> <code class="kd">public</code> <code class="nf">DynamicHookup</code><code class="o">()</code> <code class="o">{</code> <code class="n">JButton</code> <code class="n">launchButton</code> <code class="o">=</code> <code class="k">new</code> <code class="n">JButton</code><code class="o">(</code><code class="s">"Launch!"</code><code class="o">);</code> <code class="n">getContentPane</code><code class="o">().</code><code class="na">add</code><code class="o">(</code> <code class="n">launchButton</code><code class="o">,</code> <code class="s">"South"</code> <code class="o">);</code> <code class="n">getContentPane</code><code class="o">().</code><code class="na">add</code><code class="o">(</code> <code class="n">label</code><code class="o">,</code> <code class="s">"Center"</code> <code class="o">);</code> <code class="n">launchButton</code><code class="o">.</code><code class="na">addActionListener</code><code class="o">(</code> <code class="o">(</code><code class="n">ActionListener</code><code class="o">)</code><code class="n">EventHandler</code><code class="o">.</code><code class="na">create</code><code class="o">(</code> <code class="n">ActionListener</code><code class="o">.</code><code class="na">class</code><code class="o">,</code> <code class="k">this</code><code class="o">,</code> <code class="s">"launchTheMissiles"</code><code class="o">));</code> <code class="o">}</code> <code class="kd">public</code> <code class="kt">void</code> <code class="nf">launchTheMissiles</code><code class="o">()</code> <code class="o">{</code> <code class="n">label</code><code class="o">.</code><code class="na">setText</code><code class="o">(</code><code class="s">"Launched: "</code><code class="o">+</code> <code class="n">count</code><code class="o">++</code> <code class="o">);</code> <code class="o">}</code> <code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code><code class="n">String</code><code class="o">[]</code> <code class="n">args</code><code class="o">)</code> <code class="o">{</code> <code class="n">JFrame</code> <code class="n">frame</code> <code class="o">=</code> <code class="k">new</code> <code class="n">DynamicHookup</code><code class="o">();</code> <code class="n">frame</code><code class="o">.</code><code class="na">setDefaultCloseOperation</code><code class="o">(</code> <code class="n">JFrame</code><code class="o">.</code><code class="na">EXIT_ON_CLOSE</code> <code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">setSize</code><code class="o">(</code><code class="mi">150</code><code class="o">,</code> <code class="mi">150</code><code class="o">);</code> <code class="n">frame</code><code class="o">.</code><code class="na">setVisible</code><code class="o">(</code> <code class="kc">true</code> <code class="o">);</code> <code class="o">}</code> <code class="o">}</code></pre><p>Here, we call the <code class="literal">EventHandler</code>’s <code class="literal">create()</code> method, passing it the <code class="literal">ActionListener</code> class, the target object (<code class="literal">this</code>), and a string with the name of the method to invoke on the target when the event arrives. <code class="literal">EventHandler</code> internally creates a listener of the appropriate type and registers our target information. Not only do we eliminate an inner class, but the implementation of <code class="literal">EventHandler</code> may allow it to share adapters internally, producing very few objects.</p><p>This example shows how we would call a method that takes no arguments—but the <code class="literal">EventHandler</code> can actually do more, setting JavaBeans properties in response to events. The following form of <code class="literal">create()</code> tells <code class="literal">EventHandler</code> to call the <code class="literal">launchTheMissiles()</code> method, passing the <code class="literal">source</code> property of the <code class="literal">ActionEvent</code> as an argument:</p><a id="I_22_tt1241"/><pre class="programlisting"> <code class="n">EventHandler</code><code class="o">.</code><code class="na">create</code><code class="o">(</code> <code class="n">ActionListener</code><code class="o">.</code><code class="na">class</code><code class="o">,</code> <code class="n">target</code><code class="o">,</code> <code class="s">"launchTheMissiles"</code><code class="o">,</code> <code class="s">"source"</code><code class="o">)</code></pre><p>All events have a <code class="literal">source</code> property (via the <code class="literal">getSource()</code> method), but we can go further, specifying a chain of property “gets” separated by dots, which are applied before the value is passed to the method. For example:</p><a id="I_22_tt1242"/><pre class="programlisting"> <code class="n">EventHandler</code><code class="o">.</code><code class="na">create</code><code class="o">(</code> <code class="n">ActionListener</code><code class="o">.</code><code class="na">class</code><code class="o">,</code> <code class="n">target</code><code class="o">,</code> <code class="s">"launchTheMissiles"</code><code class="o">,</code> <code class="s">"source.text"</code><code class="o">)</code></pre><p>The <code class="literal">source.text</code> parameter causes the value <code class="literal">getSource().getText()</code> to be passed as an argument to <code class="literal">launchTheMissiles()</code>. In our case, that would be the label of our button. Other forms of <code class="literal">create()</code> allow more flexibility in selecting which methods of a multimethod listener interface are used as well as other options. We won’t cover every detail of the tool here.</p><div class="sect3" title="How it works"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-22-SECT-10.3.2"/>How it works</h3></div></div></div><p>The <code class="literal">EventHandler</code> uses the <a id="I_indexterm22_id824344" class="indexterm"/><code class="literal">java.lang.reflect.Proxy</code>, which is a factory that can generate adapters implementing any type of interface at runtime. By specifying one or more event listener interfaces (e.g., <code class="literal">ActionListener</code>), we get an adapter that implements those listener interfaces generated for us on the fly. The adapter is a specially created class that delegates all the method calls on its interfaces to a designated <code class="literal">InvocationHandler</code> object. See <a class="xref" href="ch01.html" title="Chapter 1. A Modern Language">Chapter 1</a> for more information about proxy classes.<a id="I_indexterm22_id824374" class="indexterm"/></p></div></div><div class="footnotes"><br/><hr/><div class="footnote"><p><sup>[<a id="ftn.learnjava3-CHP-22-FN-3" href="#learnjava3-CHP-22-FN-3" class="para">47</a>] </sup>This feature would seemingly be applicable to XML-serialized Java beans using the <code class="literal">XMLOutputStream</code> as well, but it is not currently implemented for them. This is another sign that the JavaBeans APIs have stagnated.</p></div></div></div></body></html>