+ All Categories
Home > Documents > Using Reflection in Java Craig Schock [email protected] [email protected].

Using Reflection in Java Craig Schock [email protected] [email protected].

Date post: 19-Dec-2015
Category:
View: 228 times
Download: 4 times
Share this document with a friend
45
Using Reflection in Java Craig Schock [email protected] [email protected] ary.ca
Transcript
Page 1: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Using Reflection in Java

Craig [email protected]@cpsc.ucalg

ary.ca

Page 2: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Using Reflection in Java● What is Reflection?● What people are saying about Reflection.● Three main forms of using reflection

●Reflective field/attribute determination●Reflective Instantiation●Reflective method invocation

● Basic Mechanisms of Reflection in Java

● Reflective instantiation●Simple – Object formatting example●In support of polymorphism

● Reflective method invocation●Web based forms

● Further ideas●With GUI components●With XML parsing (using SAX)

Page 3: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

What is Reflection?

● Before we can discuss what Reflection is, we need to discuss

●Binding●Static Binding vs. Dynamic Binding●Static Typing vs. Dynamic Typing

● Binding: When do symbols become bound to the entity that represents them? If I define:

int x;

when is the address of x determined?

● Compile time, load (or link) time, execution time?

Page 4: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Static Binding vs. Dynamic Binding

● Static binding is when the compiler binds the symbol to the entity which represents the symbol.

● Dynamic binding is when the connection between the symbol and the entity is determined after compilation. Usually during execution time.

●Since Java does not essentially have a link time, we will assume dynamic binding means execution time.

● Static binding is sometimes called “early binding” ● Dynamic binding is sometimes called “late binding”

Page 5: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Static Typing vs. Dynamic Typing

● Static typing is when the compiler binds a type to a symbol

● Dynamic typing is when the type of a symbol is not resolved until exection time.

myObject.aMethod() <-- Compiler knows the type of

myObject therefore, the

compiler can determine

if this invocation is

semantically valid

● Static typing is sometimes called “early typing” ● Dynamic typing is sometimes called “late typing”

Page 6: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Java – Static typing and Dynamic Binding: Polymorphism● Java is statically typed but it uses dynamic binding for polymorphism

BankAccount+deposit()+withdraw()+getMaxWithdraw()

OverdraftAccount+getMaxWithdraw()

SavingsAccount+getMaxWithdraw()

Page 7: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

public abstract class BankAccount{

private double balance;

public void deposit(double anAmount){

if (anAmount<0.0)throw new IllegalArgumentException(“Deposit cannot be negative”);

balance = balance + anAmount;}

public void withdraw(double anAmount){

if (anAmount<0.0)throw new IllegalArgumentException(“Withdraw cannot be negative”);

if (anAmount>getMaxWithdraw())throw new InvalidWithdrawAmountException(“Withdraw amount” +

anAmount + “ too large”);balance = balance - anAmount;

}

public double getBalance(){

return balance;}

private abstract double getMaxWithdraw();

}

Page 8: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

public class SavingsAccount{

private double getMaxWithdraw(){

return getBalance();}

}

public class OverdraftAccount{

private double limit;

public void setLimit(double aLimit){

limit = aLimit;}

public double getLimit(){

return limit;}

private double getMaxWithdraw(){

return getBalance() + getLimit();}

}

Page 9: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Java – Static typing and Dynamic Binding: Polymorphism● Here is where polymorphism takes place

BankAccount anAccount;

[...]

anAccount.withdraw(withdrawAmount);

public void withdraw(double anAmount){

if (anAmount<0.0)throw new IllegalArgumentException(“Withdraw cannot be negative”);

if (anAmount>getMaxWithdraw())throw new InvalidWithdrawAmountException(“Withdraw amount” +

anAmount + “ too large”);balance = balance - anAmount;

}

“anAccount” is statically typed. The compilerknows the abstract type of the object and candetermine if this method invocation is valid.

The method getMaxWithdraw is polymorphic,therefore the method is bound to an actual method dynamically

Page 10: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

What is Reflection?

● Essentially, reflection in Java is bypassing static typing.

● Java has mechanisms which allow the developer to postpone the determination of type until runtime.

● Comparison:

●In Smalltalk, all decisions are postponed until runtime.

●In Objective-C, if the compiler cannot determine type, decisions are postponed to runtime (with conditions).

●In Java, if the compiler cannot determine type, the compiler issues an error.

Page 11: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

What are people saying about Reflection?

● Sun:

First, a note of caution. Don't use the reflection API when other tools more natural to the Java programming language would suffice. For example, if you are in the habit of using function pointers in another language, you might be tempted to use the Method objects of the reflection API in the same way. Resist the temptation! Your program will be easier to debug and maintain if you don't use Method objects. Instead, you should define an interface, and then implement it in the classes that perform the needed action.

- http://java.sun.com/docs/books/tutorial/reflect/

Page 12: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

What are people saying about Reflection?

● Effective Java:Reflection allows one class to use another, even if the latter class did not exist when the former was compiled. This power, however, comes with a price:

● You lose all the benefits of compiler-time type checking, including exception checking. If a program attempts to invoke a nonexistent or inaccessible method reflectively, it will fail at run time unless you’ve taken special precautions.● The code required to perform reflective access is clumsy and verbose. It is difficult to write and difficult to read.● Performance suffers. As for release 1.3, reflective method invocation was 40 times slower on my machine than normal method invocation. Reflection was rearchitected in release 1.4 for greatly improved performance, but it is still twice as slow as normal access, and the gap is unlikely to narrow.

- Effective Java: Programming Language Guide – Joshua Bloch, pp. 158, Addison Wesley

Page 13: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

What are people saying about Reflection?

● Hibernate

Why use runtime reflection?

Every persistence mechanism needs a generic-programming mechanism for accessing attributes of the objects it persists. Serialization uses reflection. CMP uses code generation. JDO uses byte-code processing. We strongly believe that reflection (or CGLIB-style runtime byte-code generation) is the right tool for the job. Runtime reflection removes the extra (often very slow) step from the build process. And, at runtime, reflection isn't really as slow as people say. Compared to the interprocess (possibly interhost) communication and disk IO involved in every database access, reflection is lightning fast. If you don't believe me, consider the fact that people build hardcore applications in languages where every method call carries the cost of reflection.

- http://www.hibernate.org/16.html

Page 14: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

What are people saying about Reflection?

I've been a statically typed bigot for quite a few years. I learned my lesson the hard way while using C. Too many systems crashed in the field due to silly typing errors. When C++ came out, I was an avid adopter, and rabid enforcer of strong typing. I scoffed at the smalltalkers who whined about the loss of flexibility. Safety, after all, was far more important than flexibility -- and besides, we can keep our software flexible AND statically typed, if we just follow good dependency management principles.

Four years ago I got involved with Extreme Programming. I liked the pragmatic emphasis it placed upon developing software. I also liked the emphasis it put on testing. Since then I have become test infected. I can no longer concieve of writing software without using test driven development. I can't imagine not having a comprehensive suite of unit tests to back up my development.

About two years ago I noticed something. I was depending less and less on the type system for safety. My unit tests were preventing me from making type errors. The more I depended upon the unit tests, the less I depended upon the type safety of Java or C++ (my languages of choice).

I thought an experiment was in order. So I tried writing some applications in Python, and then Ruby (well known dynamically typed languages). I was not entirely surprised when I found that type issues simply never arose. My unit tests kept my code on the straight and narrow. I simply didn't need the static type checking that I had depended upon for so many years.

I also realized that the flexibility of dynamically typed langauges makes writing code significantly easier. Modules are easier to write, and easier to change. There are no build time issues at all. Life in a dynamically typed world is fundamentally simpler.

Now I am back programming in Java because the projects I'm working on call for it. But I can't deny that I feel the tug of the dynamically typed languages. I wish I was programming in Ruby or Python, or even Smalltalk. - http://www.artima.com/weblogs/viewpost.jsp?thread=4639

Page 15: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Three main forms of Reflection

● Reflective field/attribute determination●Given a reference to an object at runtime, determine its attributes●Provides you the ability to violate encapsulation

● Reflective Instantiation●Given the name of a class (and possibly parameters), create an instance of that class without having to explictly call new or having to explicitly declare a reference to the class.

● Reflective method invocation●Given a reference to an object, identify the methods it provides●Given a reference to an object, invoke a method whose name is held within a String object at runtime.

Page 16: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Basic Reflection Mechanisms in Java

● All reflection classes are in two packages●java.lang (Class)●java.lang.reflect (everything else)

● Here are all the classes you'll need:ClassConstructorFieldMethod

● Support classesArrayModifierReflectPermission

Page 17: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Where to Start?

● Obtain a reference to a “Class” object ● In the Object class, there is a method:

Class getClass()● When the JVM loads a class from a .class file, it creates an object to represent that class. ● Every object maintains a reference to its “class” object.

●Alternatively, use the Class.forName(...) method

● Provide the name of the class (including package name) as a parameter and it will return the appropriate class object.● Note: if the class is not currently loaded, it will be loaded.

Page 18: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

The class called “Class”

● [some of the] Methods defined in the class called “Class”

(static) Class forName(String className)

Constructor[] getDeclaredConstructors() Field[] getDeclaredFields() Method[] getDeclaredMethods()

Constructor getDeclaredConstructor(Class[] parameterTypes)

Field getDeclaredField(String name) Method getDeclaredMethod(String name, Class[]

parameterTypes)

Class getSuperclass() Object newInstance()

String getName()Package getPackage()

Page 19: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

The class called “Constructor”

● Important method defined in Constructor

Object newInstance(Object[] initargs)

● With this method, you can instantiate an object without invoking new

●Obtain a reference to the class object (either by calling getClass or Class.forName)●Using the class object, obtain a reference to the appropriate constructor object●Invoke the newInstance(Object[] initargs) method ●Returns a new instance of the class. The object references in the initargs array are passed (in order) to the constructor●May require cast to be usable

Page 20: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

The class called “Field”

● Important methods defined in Field

Object get(Object obj) int getInt(Object obj)long getLong(Object obj) <-- For all

fundamental types

set(Object obj, Object value)setInt(Object obj, int value)setLong(Object obj, int value) <-- For all

fundamental types

boolean isAccessible()setAccessible(boolean value)

● Given the class and a target object, the target objects fields can be queried and changed reflectively.

● Note: It is better to couple to interface rather than attributes.

Page 21: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

The class called “Method”

● Important method defined in Method

Object invoke(Object target, Object[] parameters)

● With this method, you can send a message to an object reflectively

●Obtain a reference to the class object (either by calling getClass or Class.forName)●Using the class object, obtain a reference to the appropriate method object●Invoke the invoke(Object target, Object[] parameters) method ●If the invoked method returns something, it is returned as an object●Return value may require cast

Page 22: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Let's try this baby out.

● Many programmers new to Java discover the toString() method

●After a while, they discover it is very limited●Only provides one context●Should really be used for debugging

● When programmers need to output the state of an object they can:

●Have a client query the object's get methods and format the output

●Leads to a lot of duplicated code●Formalize a system where a series of formatter objects query the target object's get methods in order to format the output.

Page 23: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Formatting example:

● A badly designed class could look like this:

Address[data attributes of Address]

[methods of address]

+formatHTML(): String+formatNormal(): String+formatXML(): String

Page 24: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

A possible solution:

● This is a good solution, but it creates a class management problem

AddressFormatter+format(Address): String

AddressHtmlFormatter+format(Address)

AddressXMLFormatter+format(Address)

AddressTabbedFormatter+format(Address)

AddressNormalFormatter+format(Address)

● Need to create an abstract formatter class for each class to be formatted● Knowing which class to instantiate creates coupling issues● This solution doesn't generalize very well without generating a lot of do-nothing code

Page 25: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

A better solution using Reflection

● We would like a solution that is easy to understand, easy to maintain and is general.

● Assume data classes are in a package● Assume we have format(...) methods defined in formatter classes. Each formatter class provides a formatting context● Structure the names of the classes so that a reflective formatting system can “compute” the names of the format classes (at runtime)● Use reflective instantiation to create the appropriate formatting object when it is needed● Will also use reflective method invocation to perform the formatting

Page 26: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Formatting system - Design

Address[data attributes of Address]

[methods of address]

org.afox

format

xml

AddressFormatter+format(Address, parameter)

[... any other formatting classes as needed]

html

AddressFormatter+format(Address, parameter)

tabbed

AddressFormatter+format(Address, parameter)

normal

AddressFormatter+format(Address, parameter)

Page 27: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Implementation using Reflective Instantiation

public static String formatChecked(Object target, String type, Object parameter) throws FormatException

{String fullClass = target.getClass().getName();String packageName = fullClass.substring(0, fullClass.lastIndexOf("."));String name = fullClass.substring(fullClass.lastIndexOf(".")+1);

try{

return invokeFormat(Class.forName(packageName + ".format." + type + "." +name + "Formatter"), target, parameter);

}catch (Exception x){

throw new FormatException(x.getClass().getName() + ":" + x.getMessage());}

}

● In this example, if the target object is an instance of org.afox.Address and the format type is “xml”, then the method will look for a formatting class called org.afox.format.xml.AddressFormatter.● If found, it will be instantiated and the format method invoked

Page 28: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Implementation using Reflective Method Invocation

private static String invokeFormat(Class aFormatClass, Object target, Object parameter) throws FormatException

{Object[] parameters = {target, parameter};try{

Class[] parameterTypes = {Class.forName("java.lang.Object"), Class.forName("java.lang.Object")};

return (String)aFormatClass.getMethod("format", parameterTypes).invoke(

aFormatClass.newInstance(),parameters);

}catch(Exception x){

throw new FormatException(x.getMessage());}

}

Page 29: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Possible additions to this solution

● Formatting each object within a given context requires the existence of an appropriate formatting class

● It is possible that generic formatters for specific contexts could be implemented using reflective field determination

Page 30: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Reflection and polymorphism

● In the previous example, reflection was used to support polymorphism.

● This can be generalized even further.

● Anytime polymorhism is used, the concrete objects must be instantiated at some point in time. If the types have to be known at compile time, much of the flexibility that polymorphism provides is lost or limited.

●You don't need to know the type of the object to invoke methods, but you do need to know the type in order to instantiate them.

Page 31: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Example: Reflection and polymorphism

● This example is a standard producer consumer problem.

●Producers read transactions from a file and put them into a queue●Consumers read transactions from the queue and execute them

● There are 3 types of transactions: CreateAccount, Deposit and Withdraw

●Sample data from a transaction file:Transaction Type Account # AmountCreateAccount 72834 1000.00Deposit 47283 153.81Withdraw 23435 296.06Deposit 23454 651.92

Page 32: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Standard procedural solution

● The standard procedural solution is to read each line and then, using an if statment, deciding what to do:

if (field[0].equals(“CreateAcount”))...

else if (field[0].equals(“Deposit”))...

else if (field[0].equals(“Withdraw”))...

● This solution is not maintainable:●What if more transaction types are added?●The above code is not cohesive and, as a result, not scalable.

● However, it is the easiest to design and implement in the trivial case.

Page 33: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

An Object Oriented solution

● An object oriented solution would be to apply the Command design pattern:

Transaction+execute()

CreateAccount+execute()

Deposit+execute()

Withdraw+execute()

● This solution is more scalable when new transaction types are added. Client code couples to the abstract Transaction type and not the concrete type.

Page 34: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

There is, however, still a problem...

● Because of static typing, we have to provide a mapping between the transaction type (which isn't known until runtime) and the instantiation of the appropriate Transaction object:Transaction xn;

if (field[0].equals(“CreateAcount”))xn = new CreateAccount(...);

else if (field[0].equals(“Deposit”))xn = new Deposit(...);

else if (field[0].equals(“Withdraw”))xn = new Withdraw(...);

● This brings us back to the same maintainability problem we had with the procedural solution. (only now, it's more complex)

● A better solution is to create a generic factory based on reflection.

Page 35: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Introducing a class called RFactory

import java.lang.reflect.*;import java.util.*; public class RFactory{ private static String basePackage = ""; public static void setBasePackage(String aBase) { basePackage = aBase; if (!basePackage.endsWith(".")) basePackage = basePackage + "."; }

Page 36: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

RFactory continued

public static Object newInstance(String className, Object[] parameters) throws InstantiationException

{try{

return getConstructor(className, getTypes(parameters)).newInstance(parameters);}catch(Exception x){

return new InstantiationException(x.getMessage());}

}

private static Class[] getTypes(Object[] parameters){

Class[] parmTypes = new Class[parameters.length];for (int i = 0; i< parmTypes.length; i++){

parmTypes[i] = parameters[i].getClass();}return parmTypes;

}

Page 37: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

RFactory continued

private static Constructor getConstructor(String className, Class[] parmTypes) throws ClassNotFoundException,

NoSuchMethodException{

Class theClass;try{

theClass = Class.forName(className);}catch (ClassNotFoundException x){

try{

theClass = Class.forName(basePackage + className);}catch(ClassNotFoundException y){

throw y;}

}return theClass.getConstructor(parmTypes);

}

Page 38: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Using RFactory

private void parseLine(String line, int count){

Object[] parameters;String[] fields = line.split("\t");

parameters = new Object[3];parameters[0] = name +"-" + count;parameters[1] = new Integer(fields[1]);parameters[2] = new Double(fields[2]);

try{

theQueue.addTransaction((Transaction)RFactory.newInstance(fields[0], parameters), name);

}catch(InstantiationException x){

System.out.println("Syntax Error on line " + count + ". Could not instantiate " + fields[0] + " transaction.");

}}

Transaction File:CreateAccount 72834 1000.00Deposit 47283 153.81Withdraw 23435 296.06Deposit 23454 651.92

Page 39: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Reflective method invocation

● The previous examples have shown reflective instantiation and reflective method invocation.

●Method invocation has been either the invocation of a constructor (thorough instantiation) or through direct invocation.

● The following will show a few more examples of reflective method invocation.

Page 40: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Web Applications: Dealing with forms

● When writing web based applications, the developer is constantly dealing with html forms.

● Html form data are strings as name-value pairs.

●When using servlets, these name-value pairs are converted to a Hashtable/Map and passed as part of the HttpServletRequest●Often, the name-value pairs have to be put into java objects through set methodsAddress anAddress = new Address();anAddress.setStreet(request.getParameter(“Street”));anAddress.setCity(request.getParameter(“City”));anAddress.setProvince(request.getParameter(“Province”));anAddress.setPostalCode(request.getParameter(“PostalCode”));

[...]

Page 41: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Resolving form parameters to set methods

● A reflective class can be created with automatically maps names to methods:

public class Resolver{

private static HashMap resolutions = new HashMap();

private HashMap methods = new HashMap();

public static void resolve(Object target, HashMap data){

Resolver aResolver = (Resolver) resolutions.get(target.getClass());if (aResolver == null){

aResolver = new Resolver(target.getClass());resolutions.put(target.getClass(), new Resolver(target.getClass()));

}

aResolver.resolveImpl(target, data);}

...

Page 42: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Resolving form parameters to set methods - continued

private Resolver(Class aClass){

setupMethods(aClass);}

private void setupMethods(Class aClass){

Method[] theMethods = aClass.getMethods();for (int i = 0; i< theMethods.length; i++){

if (theMethods[i].getName().toLowerCase().startsWith("set")){

if (theMethods[i].getParameterTypes().length == 1){

methods.put(theMethods[i].getName().toLowerCase(), theMethods[i]);

}}

}}

Page 43: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Resolving form parameters to set methods - continued

private void resolveImpl(Object anObject, HashMap data){

Object[] parms = new Object[1];Iterator keys = data.keySet().iterator();while(keys.hasNext()){

String key = (String) keys.next();Method aMethod = (Method) methods.get("set" + key.toLowerCase());parms[0] = data.get(key);try{

aMethod.invoke(anObject, parms);}catch (Exception x){

// Exceptions can be safely ignored here.}

}}

Page 44: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Further possibilities with Reflection - GUI

● Although interfaces proport to be flexible, they offer little flexibility when compared to reflection

● Example: Listeners in AWT and swing●For a given action, single method defined (actionPerformed, etc)●The same client may be registered as listener for multiple components●The method is not cohesive. It must perform dispatching duties●High coupling results

● A better solution would be based on reflection

●Components should be able to reflectively invoke the appropriate method thus eliminating the need for dispatch logic.●This type of system has been in use for many years: Smalltalk gui systems, NeXTStep (Objective-C)

Page 45: Using Reflection in Java Craig Schock schock@afox.org schock@cpsc.ucalgary.ca.

Further possibilities with Reflection – XML Parsing

● Another useful place where reflection could be used is in XML parsing.

● With SAX, every time the parse encounters a new tag, it invokes the “start” method. (defined by ContentHandler interface)

●This has the same problem as the GUI system. The start method now has to know about all sorts of different tags and what to do in each case

● A reflective solution would invoke methods directly by using the name of the tag:

●If the tag is called “street” invoke a method called “startStreet”


Recommended