DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Design Pattern: What You Need to Know to Improve Your Code Effectively
  • A Comprehensive Guide on How to Copy Objects in Python
  • Page Object Model for Performance Testing With Gatling
  • Overview of C# Async Programming

Trending

  • Message Construction: Enhancing Enterprise Integration Patterns
  • Bad Software Examples: How Much Can Poor Code Hurt You?
  • Running Unit Tests in GitHub Actions
  • Exploring the Evolution and Impact of Computer Networks
  1. DZone
  2. Coding
  3. Languages
  4. Scala Reflection Basics – Accessing an Object's Private Field Explained

Scala Reflection Basics – Accessing an Object's Private Field Explained

Konrad Malawski user avatar by
Konrad Malawski
·
Dec. 19, 13 · Interview
Like (0)
Save
Tweet
Share
7.47K Views

Join the DZone community and get the full member experience.

Join For Free

Today’s blog post will be on the slightly weird side, because as Scala developers we don’t usually need to reach into reflection to solve problems at hand. It’s easy to fight most problems with the “there’s a monad for that!” tactic, or simply “adding yet another layer solves any software development problem!”.

Sadly today’s task is not easily solvable by just that. The problem we’re going to tackle using Scala’s reflection involves a scala object and a private field we need to access.

trait MongoFlatSpec extends FlatSpec with BeforeAndAfterAll {
  var mongo: Mongo = _ // either Mongo or Fongo supplied instance here, starts up during beforeAll()
  abstract override protected def afterAll() {
    super.afterAll()
    // now it's tempting to use 
    MongoDB.close() // NOPE! Won't allow parallel execution, take a look at it's impl above.
    getMongo.close()
  }
}
class ExampleTest extends MongoFlatSpec with ShouldMatchers {
  // tests...
}

Problem statement: We need to support parallel integration tests (sbt running tests in parallel), using Mongo (or Fongo). The MongoDB object though, contains globaly shared mutable state, in form of the dbs Map, which is used to determine which MongoAddressto hit for which entity.

We are able to setup the mongo identifiers up properly for the threads executing the tests, so they don’t interfere with each other, and we won’t focus on the setup part today. Instead let’s focus on what happens during teardown. A naive implementation is a simple (ScalaTest) afterAll, like this:

trait MongoFlatSpec extends FlatSpec with BeforeAndAfterAll {
  
  var mongo: Mongo = _ // either Mongo or Fongo supplied instance here, starts up during beforeAll()
  
  abstract override protected def afterAll() {
    super.afterAll()
 
    // now it's tempting to use 
    MongoDB.close() // NOPE! Won't allow parallel execution, take a look at it's impl above.
 
    getMongo.close()
  }
}
 
class ExampleTest extends MongoFlatSpec with ShouldMatchers {
  // tests...
}

So sadly the naive implementation with using MongoDB.close() is not enough for us. Why? Imagine Two threads, running two MongoFlatSpecs, assume each has it’s own in memory Fongo instance even. There still is that shared MongoDB.dbs field deep in lift-mongodb’s internals. If we’d use MongoDB.close() we’ll clean all MongoIdentifiers, so we might break tests which are still in flight… We could try something among the lines of “wait for the right moment to clean up”, but … why wait!? There’s a ConcurrentHashMap in there, and we just want to remove “the one MongoIdentifier I have been using for this MongoFlatSpec”.

*Reflection to the rescue!* So we’ll have to use reflection, in order to execute such statement: MongoDB.dbs.remove(myMongoIdentifier). The first problem is that we’re dealing with a Scala object, also known as “Module”. Why is it also known as *Module*? Let’s take a look into the ByteCode!

scala> :javap MongoDB
public class MongoDB$ extends java.lang.Object {
    public static final MongoDB$ MODULE$; // actual instance of our `object`
    public static {};                     
    public MongoDB$();
    // ...
}
Since we want the dbs field, we will have to go through the static final MongoDB$ MODULE$ field of the MongoDB$ class. While it’s certainly doable using plain Java reflection, let’s see how Scala’s (still experimental) reflection can make this a bit nicer:
def removeFongoIdentifierFromMongoDBObject(ident: MongoIdentifier) {
  val ru = scala.reflect.runtime.universe
  val mirror = ru.runtimeMirror(getClass.getClassLoader)
  type MongosMap = ConcurrentHashMap[MongoIdentifier, MongoAddress]
  val mongoModuleSymbol = ru.typeOf[MongoDB.type].termSymbol.asModule
  val moduleMirror = mirror.reflectModule(mongoModuleSymbol)
  val instanceMirror = mirror.reflect(moduleMirror.instance)
  val dbsTerm = ru.typeOf[MongoDB.type].declaration(ru.newTermName("dbs")).asTerm.accessed.asTerm
  val fieldMirror = instanceMirror.reflectField(dbsTerm)
  val mongosMap = fieldMirror.get.asInstanceOf[MongosMap]
  mongosMap remove ident
}
The first thing to notice here is the use of Mirrors instead of explicitly callinggetDeclaredField and it’s friends on Class<?> objects like you would in plain Java APIs. Why the hassle? The grand idea behind Mirrors (other than “it sounds so cool – reflection => mirrors, yay!“) is that you can obtain either a runtime or macrosuniverse. This is discussed in detail in the environment-universes-mirrors section of the Scala docs if you want to dig deeper. The quick overview though is this: when you think about it, AST and reflection stuff are pretty similar. I mean, we traverse the same things, only at a different time (compile-time vs. runtime), so some things may be not available in the runtime, but why change the entire API if we could find some common ground – that is, Mirrors. Sure, they will be a bit different if used from the macros universe than from the runtime universe, but as we also write compile time macros the benefit is huge – only one API with slight flavoring to learn, instead of two separate ones – depending on the stage of when we apply them.

Coming back to our code though, as you can see, we were able to avoid going through the magic MODULE$ constant explicitly, it was abstracted away from us thanks to the types of mirrors we’ve been using – it’s also worth noting that for example thereflectModule(ModuleSymbol) takes a ModuleSymbol which is obtained via the “safe cast”, as one might think of it in the line above using the asModule method. The nice thing about asModule is that it will fail if the term you’re calling it on is not a module. So the API is both designed to be as typesafe as possible, even in reflection-lang, as well as “fail fast” if it detects you’re doing something wrong.

For more docs or further reference check out the docs about scala-reflection or ScalaDoc about scala.reflect.api.Mirrors.

Scala (programming language) Object (computer science) Software development

Published at DZone with permission of Konrad Malawski, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Design Pattern: What You Need to Know to Improve Your Code Effectively
  • A Comprehensive Guide on How to Copy Objects in Python
  • Page Object Model for Performance Testing With Gatling
  • Overview of C# Async Programming

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: