`
jimode2013
  • 浏览: 39885 次
社区版块
存档分类
最新评论

Thinking in java——Generics Applying a method to a sequence

 
阅读更多

Applying a method to a sequence

Reflection provides some interesting possibilities, but it relegates all the type checking to run time, and is thus undesirable in many situations. If you can achieve compile-time type checking, that's usually more desirable. But is it possible to have compile-time type checking and latent typing?

Let's look at an example that explores the problem. Suppose you want to create an apply( ) method that will apply any method to every object in a sequence. This is a situation where interfaces don't seem to fit. You want to apply any method to a collection of objects, and interfaces constrain you too much to describe "any method." How do you do this in Java?

Initially, we can solve the problem with reflection, which turns out to be fairly elegant because of Java SE5 varargs:

//: generics/Apply.java
// {main: ApplyTest}
import static net.mindview.util.Print.print;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Apply {
	public static <T, S extends Iterable<? extends T>> void apply(S seq,
			Method f, Object... args) {
		try {
			for (T t : seq)
				f.invoke(t, args);
		} catch (Exception e) {
			// Failures are programmer errors
			throw new RuntimeException(e);
		}
	}
}

class Shape {
	public void rotate() {
		print(this + " rotate");
	}

	public void resize(int newSize) {
		print(this + " resize " + newSize);
	}
}

class Square extends Shape {
}

class FilledList<T> extends ArrayList<T> {
	public FilledList(Class<? extends T> type, int size) {
		try {
			for (int i = 0; i < size; i++)
				// Assumes default constructor:
				add(type.newInstance());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}

class ApplyTest {
	public static void main(String[] args) throws Exception {
		List<Shape> shapes = new ArrayList<Shape>();
		for (int i = 0; i < 10; i++)
			shapes.add(new Shape());
		Apply.apply(shapes, Shape.class.getMethod("rotate"));
		Apply.apply(shapes, Shape.class.getMethod("resize", int.class), 5);
		List<Square> squares = new ArrayList<Square>();
		for (int i = 0; i < 10; i++)
			squares.add(new Square());
		Apply.apply(squares, Shape.class.getMethod("rotate"));
		Apply.apply(squares, Shape.class.getMethod("resize", int.class), 5);

		Apply.apply(new FilledList<Shape>(Shape.class, 10),
				Shape.class.getMethod("rotate"));
		Apply.apply(new FilledList<Shape>(Square.class, 10),
				Shape.class.getMethod("rotate"));

		SimpleQueue<Shape> shapeQ = new SimpleQueue<Shape>();
		for (int i = 0; i < 5; i++) {
			shapeQ.add(new Shape());
			shapeQ.add(new Square());
		}
		Apply.apply(shapeQ, Shape.class.getMethod("rotate"));
	}
} /* (Execute to see output) */// :~

 

In Apply, we get lucky because there happens to be an Iterable interface built into Java which is used by the Java containers library. Because of this,the apply( ) method can accept anything that implements the Iterable interface, which includes all the Collection classes such as List. But it can also accept anything else, as long as you make it Iterable—for example, the SimpleQueue class defined here and used above in main( ): 

 

 

//: generics/SimpleQueue.java
// A different kind of container that is Iterable
import java.util.*;

public class SimpleQueue<T> implements Iterable<T> {
  private LinkedList<T> storage = new LinkedList<T>();
  public void add(T t) { storage.offer(t); }
  public T get() { return storage.poll(); }
  public Iterator<T> iterator() {
    return storage.iterator();
  }
} ///:~

 In Apply.java, exceptions are converted to RuntimeExceptions because there's not much of a way to recover from exceptions—they really do represent programmer errors in this case. 

 

Note that I had to put in bounds and wildcards in order for Apply and FilledList to be used in all desired situations. You can experiment by taking these out, and you'll discover that some applications of Apply and FilledList will not work. 

FilledList presents a bit of a quandary. In order for a type to be used, it must have a default (no-arg) constructor. Java has no way to assert such a thing at compile time, so it becomes a runtime issue. A common suggestion to ensure compile-time checking is to define a factory interface that has a method that generates objects; then FilledList would accept that interface rather than the "raw factory" of the type token. The problem with this is that all the classes you use in FilledList must then implement your factory interface.Alas, most classes are created without knowledge of your interface, and therefore do not implement it. Later, I'll show one solution using adapters. 

But the approach shown, of using a type token, is perhaps a reasonable trade-off (at least as a first-cut solution). With this approach, using something like FilledList is just easy enough that it may be used rather than ignored. Of course, because errors are reported at run time, you need confidence that 

these errors will appear early in the development process. 

Note that the type token technique is recommended in the Java literature,such as Gilad Bracha's paper Generics in the Java Programming Language,where he notes, "It's an idiom that's used extensively in the new APIs for manipulating annotations, for example." However, I've discovered some inconsistency in people's comfort level with this technique; some people strongly prefer the factory approach, which was presented earlier in this chapter.

Also, as elegant as the Java solution turns out to be, we must observe that the use of reflection (although it has been improved significantly in recent versions of Java) may be slower than a non-reflection implementation, since so much is happening at run time. This should not stop you from using the 

solution, at least as a first cut (lest you fall sway to premature optimization),but it's certainly a distinction between the two approaches. 

 

 

分享到:
评论

相关推荐

    Thinking in Java 4th Edition + Annotated Solution Guide (代码)英文文字版 带书签 有答案

    《Thinking in Java》是Bruce Eckel的经典之作,第四版涵盖了Java编程语言的广泛主题,适合初学者和有经验的程序员。这本书深入浅出地讲解了Java的核心概念和技术,旨在帮助读者建立坚实的编程基础,并理解面向对象...

    Thinking in Java中文版(3和4)

    《Thinking in Java》是Java编程领域的一本经典著作,由Bruce Eckel撰写,深受程序员喜爱。这本书分为第三版和第四版,提供了英文版和中文版,适合不同语言背景的学习者。书中内容详实且深入,从基础知识到高级概念...

    Thinking in java 22

    很抱歉,但根据您给出的信息,这似乎是一个音乐文件列表,而非与"Thinking in Java"相关的IT知识内容。"Thinking in Java"是一本著名的编程书籍,通常与Java编程语言的学习和实践相关。如果您的目标是获取这方面的...

    Thinking in Java 课文中的代码

    《Thinking in Java》是Bruce Eckel的经典编程教材,它深入浅出地介绍了Java语言的核心概念和技术。这本书通过实例代码来讲解理论,使读者能够更好地理解和掌握Java编程。在这个压缩包中,我们很可能会找到与书中的...

    Thinking in Java 3th and 4th Edition+代码+答案

    《Thinking in Java》是Bruce Eckel的经典编程教材,它涵盖了Java语言的核心概念和技术,深受程序员和初学者喜爱。这本书分为第三版和第四版,每个版本都有其独特的知识点和更新内容。 第三版是针对Java 2 Platform...

    Thinking in Java(英文原版第4版)

    《Thinking in Java(英文原版第4版)》作为一本经典Java编程思想书籍,其内容涵盖了面向对象的叙述方式,并针对Java SE5/6版本新增了示例和章节。本书适合作为初学者的入门教材,同时也包含了足够的深度,适合专业...

    Thinking in Java英文版.rar

    《Thinking in Java》会详细解释如何使用这些集合以及迭代器(Iterator)和泛型(Generics)的概念,这些对于编写高效且可维护的代码至关重要。 多线程是并发编程的基础,Java提供了强大的支持。书中会讨论线程的...

    Thinking in java 4th eidtion

    ### 《Thinking in Java》第四版关键知识点综述 #### 一、书籍概述与评价 《Thinking in Java》第四版是一本备受推崇的经典Java编程教材,由Bruce Eckel撰写,适用于初学者及进阶读者。本书自出版以来,便成为众多...

    Thinking In Java 第四版

    《Thinking in Java》是Bruce Eckel的经典之作,第四版更是深受全球Java开发者喜爱的教材。这本书深入浅出地讲解了Java编程语言的核心概念和技术,旨在为初学者提供全面且深入的Java学习指导。 首先,书中的第一章...

    Thinking in Java 4th Edition with Annotated Solution Guide编程思想+课后习题源码

    《Thinking in Java 4th Edition with Annotated Solution Guide》是一本经典的Java编程教材,由Bruce Eckel撰写。这本书深入浅出地介绍了Java编程的核心概念和技术,对于初学者和经验丰富的开发者来说,都是一个...

    Thinking in Java 4th 习题答案

    《Thinking in Java 4th 习题答案》涵盖了多个关键的Java编程概念和技术,包括深入的容器理解、输入/输出(I/O)处理、枚举类型(Enumerated Types)、注解(Annotations)以及并发编程(Concurrency)。这些章节是Java学习...

    Thinking in Java第三版+第四版(中文版+习题答案)

    《Thinking in Java》是Java编程领域的一本经典之作,由Bruce Eckel撰写,深受程序员喜爱。这本书深入浅出地介绍了Java语言的核心概念和技术,旨在帮助读者建立起坚实的基础,并提升编程思维能力。书中不仅包含了...

    Java Generics and Collections (Java泛型与集合)

    本书"Java Generics and Collections"深入探讨了这两个主题,帮助开发者编写更安全、更高效且可维护的代码。 首先,让我们来理解Java泛型。泛型是Java 5引入的一项特性,它允许在类、接口和方法中使用类型参数。这...

    Generics_in_the_Java_Programming_Language译文

    Java 泛型详解 Java 中的泛型是 Java 5(JDK 1.5)中引入的一项新特性,旨在解决类型安全和代码重用的问题。泛型允许程序员对类型进行抽象,使得代码更加灵活和可维护。 泛型的优点: 1. 类型安全:泛型可以在...

    thinking in java 4th 习题答案

    ### Thinking in Java 第四版习题解答概览 #### 目录解读 - **Everything is an Object(一切皆对象)**:本章介绍Java的基本概念,即Java是一种面向对象的语言,所有的基本数据类型都可以通过包装类转换为对象,...

    Thinking In Java 练习题答案 第四版

    《Thinking In Java》是Bruce Eckel的经典之作,它深入浅出地介绍了Java编程语言的核心概念和技术,被广大程序员视为学习Java的权威指南。第四版更是加入了更多现代Java特性,如Generics、Annotations等,使读者能够...

Global site tag (gtag.js) - Google Analytics