How can the Value in Java Map be of any type? mapvalue

Source: Internet
Author: User

(Reproduced) How can the Value in Java Map be of any type? mapvalue

Reprint address: http://www.importnew.com/15556.html if there is infringement, please contact the author to delete in time.

I moved to my blog to have time to savor and play.

 

This article is translated from javacodegeeks by ImportNew-shutear. Welcome to the translation team. For more information, see the requirements at the end of this document.

In general, developers occasionally encounter this situation: ing any type of value in a specific container. However, Java Collection APIs only provide parameterized containers. This restricts the Safe Use of HashMap for types, such as a single value type. But what should I do if I want to mix apples and pears?

Fortunately, there is a simple design pattern that allows you to map different value types using Java generics, Joshua Bloch in its aggressive Java (second edition, 29th items) describe it as a type-safe heterogeneous container (Typesafe hetereogeneous container).

I have encountered some unsuitable solutions for this topic recently. It gave me an explanation of the problem domain in this article and explained some implementation details.

Use Java generic ing to map different value types

For example, you need to provide the context of an application, which can bind a specific key to any type of value. Using String as the HashMap of the key, a simple type safe implementation may be as follows:

1234567891011121314 public class Context {   private final Map<String,Object> values = new HashMap<>();   public void put( String key, Object value ) {    values.put( key, value );  }   public Object get( String key ) {    return values.get( key );  }   [...]}

The following code snippet shows how to useContext:

123456 Context context = new Context();Runnable runnable = ...context.put( "key", runnable ); // several computation cycles later...Runnable value = ( Runnable )context.get( "key" );

It can be seen that the disadvantage of this method is that the downward Transformation (down cast) needs to be performed in line 6th ). If you replace the value type of the key-value pair,ClassCastExceptionException:

12345678910 Context context = new Context();Runnable runnable = ...context.put( "key", runnable ); // several computation cycles later...Executor executor = ...context.put( "key", executor ); // even more computation cycles later...Runnable value = ( Runnable )context.get( "key" ); // runtime problem

The cause of this problem is hard to trace, because the implementation steps may be widely distributed in various parts of your program.

To improve this situation, it seems reasonable to bind value to its key and value.

Among the various solutions that I have seen and follow this method, common errors are more or less attributed to the following:ContextVariants:

1234567891011121314 public class Context {   private final <String, Object> values = new HashMap<>();   public <T> void put( String key, T value, Class<T> valueType ) {    values.put( key, value );  }   public <T> T get( String key, Class<T> valueType ) {    return ( T )values.get( key );  }   [...]}

The same basic usage may be as follows:

123456 Context context = new Context();Runnable runnable = ...context.put( "key", runnable, Runnable.class ); // several computation cycles later...Runnable value = context.get( "key", Runnable.class );

At first glance, this Code may give you the illusion of more types of security, because it avoids downcast in line 2 ). However, running the following code will bring us back to reality, because we will still fall into the value assignment statement at line 1.ClassCastExceptionEmbrace:

12345678910 Context context = new Context();Runnable runnable = ...context.put( "key", runnable, Runnable.class ); // several computation cycles later...Executor executor = ...context.put( "key", executor, Executor.class ); // even more computation cycles later...Runnable value = context.get( "key", Runnable.class ); // runtime problem

What's wrong?

First, the downward transformation in Context # get is invalid because the type erasure replaces the unbonded parameters parameter with the static transformation Object ). In addition, more importantly, this implementation does not useContext#putType information. At best, this is an extra beauty.

Type-safe heterogeneous containers

Although the aboveContextThe variant does not work, but indicates the direction. The following question is: how can we reasonably parameterize this key? To answer this question, let's take a look at a simple implementation of typesafe heterogenous container pattern based on the Type Security heterogeneous container model described by Bloch.

Our idea is to use the key itself.classType as the key. BecauseClassIs a parameterized type, which ensures that the Context method is type-safe without the need to resort to an unchecked forced conversionT. In this formClassThe object is called a type token ).

1234567891011121314 public class Context {   private final Map<Class<?>, Object> values = new HashMap<>();   public <T> void put( Class<T> key, T value ) {    values.put( key, value );  }   public <T> T get( Class<T> key ) {    return key.cast( values.get( key ) );  }   [...]}

Note thatContext#getHow to replace the downward transformation with an effective dynamic variable. The client can use this context as follows:

12345678910 Context context = new Context();Runnable runnable ...context.put( Runnable.class, runnable ); // several computation cycles later...    Executor executor = ...context.put( Executor.class, executor ); // even more computation cycles later...Runnable value = context.get( Runnable.class );

The client code will work normally this time and there will be no class conversion issues, because it is impossible to exchange a key-value pair through a different value type.

Where there is light, there must be shadows, and where there is shadow, there must be light. There is no shadow or shadow. Murakami chunshu

Bloch points out that this pattern has two limitations. "First, malicious clients can use class objects in raw forms to easily break type security ." To ensure the type security during runtime, you canContext#putUse dynamic cast ).

123 public <T> void put( Class<T> key, T value ) {  values.put( key, key.cast( value ) );}

The second limitation is that it cannot be used in non-Concrete (Non-reifiable). In other words, you can saveRunnableOrRunnable[]But cannot saveList<Runnable>.

This is becauseList<Runnable>No specific class Object. All parameterized types refer to the sameList.classObject. Therefore, Bloch points out that there is no satisfactory solution to this limitation.

But what if you need to store two entries with the same value type? If you only want to store a type-Safe Container, you can consider creating a new type extension, but this is obviously not the best design. Using custom keys may be a better solution.

Multiple container entries of the same type

To store multiple container entries of the same type, we can use the custom key to changeContextClass. This key must provide the type information required for data type security and identify different value objects. AStringThe naive key implementation for instances may be like this:

12345678910 public class Key<T> {   final String identifier;  final Class<T> type;   public Key( String identifier, Class<T> type ) {    this.identifier = identifier;    this.type = type;  }}

We use parameterizedClassAs a hook for type information, the adjusted Context uses parameterizedKeyInsteadClass.

1234567891011121314 public class Context {   private final Map<Key<?>, Object> values = new HashMap<>();   public <T> void put( Key<T> key, T value ) {    values.put( key, value );  }   public <T> T get( Key<T> key ) {    return key.type.cast( values.get( key ) );  }   [...]}

The client will use this versionContext

1234567891011121314 Context context = new Context(); Runnable runnable1 = ...Key<Runnable> key1 = new Key<>( "id1", Runnable.class );context.put( key1, runnable1 ); Runnable runnable2 = ...Key<Runnable> key2 = new Key<>( "id2", Runnable.class );context.put( key2, runnable2 ); // several computation cycles later...Runnable actual = context.get( key1 ); assertThat( actual ).isSameAs( runnable1 );

Although this code snippet is available, it still has defects. InContext#getMedium,KeyUsed as a query parameter. Use the same identifier and class to initialize two differentKeyInstance, one for put, the other for get, and the lastgetOperation will returnnull. This is not what we want ......

123456789 // The translator attaches a code snippet.Context context = new Context(); Runnable runnable1 = ...Key<Runnable> key1 = new Key<>( "same-id", Runnable.class );Key<Runnable> key2 = new Key<>( "same-id", Runnable.class );context.put( key1, runnable1 );// One for put context.get(key2); // The other is used for get --> return null;

Fortunately,KeyDesign appropriateequalsAndhashCodeYou can easily solve this problem and thenHashMapSearch for jobs as expected. Finally, you can provide a factory method for key creation to simplify the creation process (useful when used together with static import ):

123 public staticKey key( String identifier, Class type ) {  return new Key( identifier, type );}
Conclusion

"The collection API illustrates the general usage of generics and limits that each container can have only a fixed number of type parameters. You can avoid this restriction by placing the type parameter on the key instead of the container. For this type of secure heterogeneous containers, Class can be used as the key ." (Joshua Bloch, 29th objective Java ).

There is nothing to add to the above acronyms, except to wish you a successful mix of Apple and pear ......

Original article: javacodegeeks Translation: ImportNew.com-shutear
Http://www.importnew.com/15556.html.
[For reprinting, Please retain the original source, translator, and translation links.]

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.