集合类型在面向对象编程中很常用,这也带来一些代码相关的问题。比如,“怎么操作集合中不同类型的对象?”
一种做法就是遍历集合中的每个元素,然后根据它的类型而做具体的操作。这会很复杂,尤其当你不知道集合中元素的类型时。如果y要打印集合中的元素,可以写一个这样的方法:
public void messyPrintCollection(Collection collection) { Iterator iterator = collection.iterator() while (iterator.hasNext()) System.out.println(iterator.next().toString()) }
看起来很简单。仅仅调用了Object.toString()方法并打印出了对象,对吧?但如果你的集合是一个包含hashtable的vector呢?那会变得更复杂。你必须检查集合返回对象的类型:
public void messyPrintCollection(Collection collection) { Iterator iterator = collection.iterator() while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Collection) messyPrintCollection((Collection)o); else System.out.println(o.toString()); } }
好了,现在可以处理内嵌的集合对象,但其他对象返回的字符串不是你想要的呢?假如你想在字符串对象加上引号,想在Float对象后加一个f,你该怎么做?代码会变得更加复杂:
public void messyPrintCollection(Collection collection) { Iterator iterator = collection.iterator() while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Collection) messyPrintCollection((Collection)o); else if (o instanceof String) System.out.println("'"+o.toString()+"'"); else if (o instanceof Float) System.out.println(o.toString()+"f"); else System.out.println(o.toString()); } }
代码很快就变杂乱了。你不想让代码中包含一大堆的if-else语句!怎么避免呢?访问者模式可以帮助你。
为实现访问者模式,你需要创建一个Visitor接口,为被访问的集合对象创建一个Visitable接口。接下来需要创建具体的类来实现Visitor和Visitable接口。这两个接口大致如下:
public interface Visitor { public void visitCollection(Collection collection); public void visitString(String string); public void visitFloat(Float float); } public interface Visitable { public void accept(Visitor visitor); }
对于一个具体的String类,可以这么实现:
public class VisitableString implements Visitable { private String value; public VisitableString(String string) { value = string; } public void accept(Visitor visitor) { visitor.visitString(this); } }
在accept方法中,根据不同的类型,调用visitor中对应的方法:
visitor.visitString(this)
具体Visitor的实现方式如下:
public class PrintVisitor implements Visitor { public void visitCollection(Collection collection) { Iterator iterator = collection.iterator(); while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Visitable) ((Visitable)o).accept(this); } public void visitString(String string) { System.out.println("'"+string+"'"); } public void visitFloat(Float float) { System.out.println(float.toString()+"f"); } }
到时候,只要实现了VisitableFloat类和VisitableCollection类并调用合适的visitor方法,你就可以去掉包含一堆if-else结构的messyPrintCollection方法,采用一种十分清爽的方式实现了同样的功能。visitCollection()方法调用了Visitable.accept(this),而accept()方法又反过来调用了visitor中正确的方法。这就是双分派:Visitor调用了一个Visitable类中的方法,这个方法又反过来调用了Visitor类中的方法。
尽管实现visitor后,if-else语句不见了,但还是引入了很多附加的代码。你不得不将原始的对象——String和Float,打包到一个实现Visitable接口的类中。虽然很烦人,但这一般来说不是个问题。因为你可以限制被访问集合只能包含Visitable对象。
然而,这还有很多附加的工作要做。更坏的是,当你想增加一个新的Visitable类型时怎么办,比如VisitableInteger?这是访问者模式的一个主要缺点。如果你想增加一个新的Visitable类型,你不得不改变Visitor接口以及每个实现Visitor接口方法的类。你可以不把Visitor设计为接口,取而代之,可以把Visitor设计为一个带有空操作的抽象基类。这与Java GUI中的Adapter类很相似。这么做的问题是你会用尽单次继承,而常见的情形是你还想用继承实现其他功能,比如继承StringWriter类。这同样只能成功访问实现Visitable接口的对象。
幸运的是,Java可以让你的访问者模式更灵活,你可以按你的意愿增加Visitable对象。怎么实现呢?答案是使用反射。使用反射的ReflectiveVisitor接口只需要一个方法:
public interface ReflectiveVisitor { public void visit(Object o); }
好了,上面很简单。Visitable接口先不动,待会我会说。现在,我使用反射实现PrintVisitor类。
public class PrintVisitor implements ReflectiveVisitor { public void visitCollection(Collection collection) { ... same as above ... } public void visitString(String string) { ... same as above ... } public void visitFloat(Float float) { ... same as above ... } public void default(Object o) { System.out.println(o.toString()); } public void visit(Object o) { // Class.getName() returns package information as well. // This strips off the package information giving us // just the class name String methodName = o.getClass().getName(); methodName = "visit"+ methodName.substring(methodName.lastIndexOf('.')+1); // Now we try to invoke the method visit<methodName> try { // Get the method visitFoo(Foo foo) Method m = getClass().getMethod(methodName, new Class[] { o.getClass() }); // Try to invoke visitFoo(Foo foo) m.invoke(this, new Object[] { o }); } catch (NoSuchMethodException e) { // No method, so do the default implementation default(o); } } }
现在你无需使用Visitable包装类(包装了原始类型String、Float)。你可以直接访问visit(),它会调用正确的方法。visit()的一个优点是它会分派它认为合适的方法。这不一定使用反射,可以使用完全不同的一种机制。
在新的PrintVisitor类中,有对应于Collections、String和Float的操作方法;对于不能处理的类型,可以通过catch语句捕捉。对于不能处理的类型,可以通过扩展visit()方法来尝试处理它们的所有超类。首先,增加一个新的方法getMethod(Class c),返回值是一个可被触发的方法。它会搜索Class c的所有父类和接口,以找到一个匹配方法。
protected Method getMethod(Class c) { Class newc = c; Method m = null; // Try the superclasses while (m == null && newc != Object.class) { String method = newc.getName(); method = "visit" + method.substring(method.lastIndexOf('.') + 1); try { m = getClass().getMethod(method, new Class[] {newc}); } catch (NoSuchMethodException e) { newc = newc.getSuperclass(); } } // Try the interfaces. If necessary, you // can sort them first to define 'visitable' interface wins // in case an object implements more than one. if (newc == Object.class) { Class[] interfaces = c.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { String method = interfaces[i].getName(); method = "visit" + method.substring(method.lastIndexOf('.') + 1); try { m = getClass().getMethod(method, new Class[] {interfaces[i]}); } catch (NoSuchMethodException e) {} } } if (m == null) { try { m = thisclass.getMethod("visitObject", new Class[] {Object.class}); } catch (Exception e) { // Can't happen } } return m; }
这看上去很复杂,实际上并不。大致来说,首先根据传入的class名称搜索可用方法;如果没找到,就尝试从父类搜索;如果还没找到,就从接口中尝试。最后,(仍没找到)可以使用visitObject()作为默认方法。
由于大家对传统的访问者模式比较熟悉,这里沿用了之前方法命名的惯例。但是,有些人可能注意到,把所有的方法都命名为“visit”并通过参数类型不同来区分,这样更高效。然而,如果你这么做,你必须把visit(Object o)方法的名称改为其他,比如dispatch(Object o)。否则,(当没有对应处理方法时),你无法退回到默认的处理方法,并且当你调用visit(Object o)方法时,为了确保正确的方法调用,你必须将参数强制转化为Object。
为了利用getMethod()方法,现在需要修改一下visit()方法。
public void visit(Object object) { try { Method method = getMethod(getClass(), object.getClass()); method.invoke(this, new Object[] {object}); } catch (Exception e) { } }
现在,visitor类更加强大了——可以传入任意的对象并且有对应的处理方法。另外,有一个默认处理方法,visitObject(Object o),的好处就是就可以捕捉到任何没有明确说明的类型。再稍微修改下,你甚至可以添加一个visitNull()方法。
我仍保留Visitable接口是有原因的。传统访问者模式的另一个好处是它可以通过Visitable对象控制对象结构的遍历顺序。举例来说,假如有一个实现了Visitable接口的类TreeNode,它在accept()方法中遍历自己的左右节点。
public void accept(Visitor visitor) { visitor.visitTreeNode(this); visitor.visitTreeNode(leftsubtree); visitor.visitTreeNode(rightsubtree); }
这样,只要修改下Visitor类,就可以通过Visitable类控制遍历:
public void visit(Object object) throws Exception { Method method = getMethod(getClass(), object.getClass()); method.invoke(this, new Object[] {object}); if (object instanceof Visitable) { callAccept((Visitable) object); } } public void callAccept(Visitable visitable) { visitable.accept(this); }
如果你实现了Visitable对象的结构,你可以保持callAccept()不变,就可以使用Visitable控制的对象遍历。如果你想在visitor中遍历对象结构,你只需重写allAccept()方法,让它什么都不做。
当使用几个不同的visitor去操作同一个对象集合时,访问者模式的力量就会展现出来。比如,当前有一个解释器、中序遍历器、后续遍历器、XML编写器以及SQL编写器,它们可以处理同一个对象集合。我可以轻松地为这个集合再写一个先序遍历器或者一个SOAP编写器。另外,它们可以很好地兼容它们不识别的类型,或者我愿意的话可以让它们抛出异常。
总结
使用Java反射,可以使访问者模式提供一种更加强大的方式操作对象结构,可以按照需求灵活地增加新的Visitable
类型。我希望在你的编程之旅中可以使用访问者模式。
위 내용은 Java는 방문자 모드에서 리플렉션을 사용하여 샘플 코드 공유를 구현합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

이 기사에서는 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 및 Gradle을 사용하여 접근 방식과 최적화 전략을 비교합니다.

이 기사에서는 Maven 및 Gradle과 같은 도구를 사용하여 적절한 버전 및 종속성 관리로 사용자 정의 Java 라이브러리 (JAR Files)를 작성하고 사용하는 것에 대해 설명합니다.

이 기사는 카페인 및 구아바 캐시를 사용하여 자바에서 다단계 캐싱을 구현하여 응용 프로그램 성능을 향상시키는 것에 대해 설명합니다. 구성 및 퇴거 정책 관리 Best Pra와 함께 설정, 통합 및 성능 이점을 다룹니다.

이 기사는 캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA를 사용하는 것에 대해 설명합니다. 잠재적 인 함정을 강조하면서 성능을 최적화하기위한 설정, 엔티티 매핑 및 모범 사례를 다룹니다. [159 문자]

Java의 클래스 로딩에는 부트 스트랩, 확장 및 응용 프로그램 클래스 로더가있는 계층 적 시스템을 사용하여 클래스로드, 링크 및 초기화 클래스가 포함됩니다. 학부모 위임 모델은 핵심 클래스가 먼저로드되어 사용자 정의 클래스 LOA에 영향을 미치도록합니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

드림위버 CS6
시각적 웹 개발 도구

맨티스BT
Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

DVWA
DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

MinGW - Windows용 미니멀리스트 GNU
이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

SecList
SecLists는 최고의 보안 테스터의 동반자입니다. 보안 평가 시 자주 사용되는 다양한 유형의 목록을 한 곳에 모아 놓은 것입니다. SecLists는 보안 테스터에게 필요할 수 있는 모든 목록을 편리하게 제공하여 보안 테스트를 더욱 효율적이고 생산적으로 만드는 데 도움이 됩니다. 목록 유형에는 사용자 이름, 비밀번호, URL, 퍼징 페이로드, 민감한 데이터 패턴, 웹 셸 등이 포함됩니다. 테스터는 이 저장소를 새로운 테스트 시스템으로 간단히 가져올 수 있으며 필요한 모든 유형의 목록에 액세스할 수 있습니다.
