blob: edbf67d56b161eb5546698cf82a6d389bf49a8c9 [file] [log] [blame]
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--><head>
<meta charset='utf-8'/><meta http-equiv='X-UA-Compatible' content='IE=edge'/><meta name='viewport' content='width=device-width, initial-scale=1'/><meta name='keywords' content='type checking, printf, regex, format'/><meta name='description' content='This post looks at Groovy&apos;s Type Checkers.'/><title>The Apache Groovy programming language - Blogs - Groovy Type Checkers</title><link href='../img/favicon.ico' type='image/x-ico' rel='icon'/><link rel='stylesheet' type='text/css' href='../css/bootstrap.css'/><link rel='stylesheet' type='text/css' href='../css/font-awesome.min.css'/><link rel='stylesheet' type='text/css' href='../css/style.css'/><link rel='stylesheet' type='text/css' href='https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css'/>
</head><body>
<div id='fork-me'>
<a href='https://github.com/apache/groovy'>
<img style='position: fixed; top: 20px; right: -58px; border: 0; z-index: 100; transform: rotate(45deg);' src='/img/horizontal-github-ribbon.png'/>
</a>
</div><div id='st-container' class='st-container st-effect-9'>
<nav class='st-menu st-effect-9' id='menu-12'>
<h2 class='icon icon-lab'>Socialize</h2><ul>
<li>
<a href='https://groovy-lang.org/mailing-lists.html' class='icon'><span class='fa fa-envelope'></span> Discuss on the mailing-list</a>
</li><li>
<a href='https://twitter.com/ApacheGroovy' class='icon'><span class='fa fa-twitter'></span> Groovy on Twitter</a>
</li><li>
<a href='https://groovy-lang.org/events.html' class='icon'><span class='fa fa-calendar'></span> Events and conferences</a>
</li><li>
<a href='https://github.com/apache/groovy' class='icon'><span class='fa fa-github'></span> Source code on GitHub</a>
</li><li>
<a href='https://groovy-lang.org/reporting-issues.html' class='icon'><span class='fa fa-bug'></span> Report issues in Jira</a>
</li><li>
<a href='http://stackoverflow.com/questions/tagged/groovy' class='icon'><span class='fa fa-stack-overflow'></span> Stack Overflow questions</a>
</li><li>
<a href='http://groovycommunity.com/' class='icon'><span class='fa fa-slack'></span> Slack Community</a>
</li>
</ul>
</nav><div class='st-pusher'>
<div class='st-content'>
<div class='st-content-inner'>
<!--[if lt IE 7]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]--><div><div class='navbar navbar-default navbar-static-top' role='navigation'>
<div class='container'>
<div class='navbar-header'>
<button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
<span class='sr-only'></span><span class='icon-bar'></span><span class='icon-bar'></span><span class='icon-bar'></span>
</button><a class='navbar-brand' href='../index.html'>
<i class='fa fa-star'></i> Apache Groovy
</a>
</div><div class='navbar-collapse collapse'>
<ul class='nav navbar-nav navbar-right'>
<li class=''><a href='https://groovy-lang.org/learn.html'>Learn</a></li><li class=''><a href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li class=''><a href='/download.html'>Download</a></li><li class=''><a href='https://groovy-lang.org/support.html'>Support</a></li><li class=''><a href='/'>Contribute</a></li><li class=''><a href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li class=''><a href='/blog'>Blog posts</a></li><li class=''><a href='https://groovy.apache.org/events.html'></a></li><li>
<a data-effect='st-effect-9' class='st-trigger' href='#'>Socialize</a>
</li><li class=''>
<a href='../search.html'>
<i class='fa fa-search'></i>
</a>
</li>
</ul>
</div>
</div>
</div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Groovy Type Checkers</a></li><li><a href='#_an_example' class='anchor-link'>An Example</a></li><li><a href='#_conclusion' class='anchor-link'>Conclusion</a></li></ul></div><div class='col-lg-8 col-lg-pull-0'><a name='doc'></a><h1>Groovy Type Checkers</h1><p><span>Author: <i>Paul King</i></span><br/><span>Published: 2024-01-20 08:30PM</span></p><hr/><div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>By default, Groovy only performs minimal static type checking
during compilation. This allows you to write scripts which
make use of very dynamic features of the language,
like adding methods at runtime. But many programs don&#8217;t
make use of such dynamic features, so Groovy allows
you to ramp up the amount of type checking the compiler will
do when you need to, by using Groovy&#8217;s
<a href="https://docs.groovy-lang.org/docs/latest/html/documentation/#static-type-checking">static type checking</a> features.</p>
</div>
<div class="paragraph">
<p>But rather than just providing a dynamic/static (or on/off)
type checking capability, Groovy provides a
<a href="https://docs.groovy-lang.org/docs/groovy-5.0.0-alpha-5/html/documentation/#_type_checking_extensions">type checking extension</a> mechanism. It turns out there are many scenarios
where you want mostly Java-like type checking but some minor
relaxations for just a <em>light sprinkling</em> of dynamic behavior.
Conversely, there are times when you want <em>stronger-than-Java</em> checking.
Here we will look at two built-in checkers for this latter case.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The <a href="https://docs.groovy-lang.org/docs/groovy-5.0.0-alpha-5/html/documentation/#_checking_regular_expressions">RegexChecker</a> was introduced in Groovy 4. It performs additional checks when using Groovy&#8217;s regex operators or calling Java regex API calls.
It looks for illegal characters, unclosed capturing groups or ranges, illegal or unknown syntax and many other errors that would normally occur at runtime. Using the checker ensures that if your program compiles, you won&#8217;t receive a runtime error.</p>
</li>
<li>
<p>The <a href="https://docs.groovy-lang.org/docs/groovy-5.0.0-alpha-5/html/documentation/#_checking_format_strings">FormatStringChecker</a>
is available in alpha releases of Groovy 5. It
checks various printf and format methods. It looks for illegal precision
specifications, unknown format conversions, type conversion mismatches
and other errors. When using the checker, if your program compiles, you should
not receive such errors.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Both these checkers rely on having the regular expression or format string
available somewhere in the code. If you store such expressions in databases
or properties files, or somewhere else, then you are out of luck.
But remember, the mechanism is extensible, so adding further support
is only a few lines (or possibly tens of lines) of code away.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_an_example">An Example</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let&#8217;s look at an example which uses both those checkers. It is an example
which uses regex to pull some information out of a log message from
the first commit in Groovy&#8217;s source code repository. For simplicity,
we duplicated the log message as a String but Groovy has nice capabilities
if we wanted to extract that from git ourselves.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="prettyprint highlight"><code data-lang="groovy">var firstCommitLog = 'Date: Thu Aug 28 18:48:39 2003 +0000' // <b class="conum">(1)</b>
@TypeChecked(extensions = 'groovy.typecheckers.RegexChecker') // <b class="conum">(2)</b>
def getMatcher(String text) {
text =~ /Date:\s*(\w{3})\s(\w{3})\s(\d{2})\s(\d{2}):(\d{2}):(\d{2})\s(\d{4})/
}
@TypeChecked(extensions = 'groovy.typecheckers.FormatStringChecker') // <b class="conum">(3)</b>
def displayInfo(String day, boolean afternoon, BigDecimal numYears) {
printf 'Day: %s, Afternoon: %x, Years ago: %3.1f', day, afternoon, numYears
}
var (_, day, month, date, hour, _, _, year) = getMatcher(firstCommitLog)[0] // <b class="conum">(4)</b>
var afternoon = hour.toInteger() &gt;= 12
int monthNumber = DateTimeFormatter.ofPattern('MMM').parse(month)[MONTH_OF_YEAR]
var now = LocalDate.now()
var firstCommitDate = LocalDate.of(year.toInteger(), monthNumber, date.toInteger())
var numYears = (now - firstCommitDate) / 365 // <b class="conum">(5)</b>
displayInfo(day, afternoon, numYears)</code></pre>
</div>
</div>
<div class="colist arabic">
<ol>
<li>
<p>One of the lines in the first git commit message</p>
</li>
<li>
<p>A method with additional regex checking</p>
</li>
<li>
<p>A method with additional format string checking</p>
</li>
<li>
<p>Extract components from first match</p>
</li>
<li>
<p>Calculate the number of years since the first commit as a floating-point number</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>When we run this script we get the following output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Day: Thu, Afternoon: true, Years ago: 20.4</pre>
</div>
</div>
<div class="paragraph">
<p>So, we know the first commit happened over 20 years ago on a Thursday afternoon/evening.</p>
</div>
<div class="paragraph">
<p>Here we annotated the two methods in question with <code>TypeChecked</code>,
and explicitly listed out the <code>extensions</code> we wanted in each case.
Groovy provides programmatic
<a href="https://docs.groovy-lang.org/docs/groovy-5.0.0-alpha-5/html/documentation/#compilation-customizers">compiler configuration</a>
and
<a href="https://docs.groovy-lang.org/docs/groovy-5.0.0-alpha-5/html/documentation/#_the_configscript_commandline_parameter">configuration
scripts</a>
to potentially hide away such details, but we won&#8217;t go into further details here.</p>
</div>
<div class="paragraph">
<p>Let&#8217;s have a look at some of the compile-time errors we would see
if we had made errors in the script.</p>
</div>
<div class="paragraph">
<p>Let&#8217;s put an accidental closing grouping bracket at the start of the expression:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>[Static type checking] - Bad regex: Unmatched closing ')'
)Date:\s*(\w{3})\s(\w{3})\s(\d{2})\s(\d{2}):(\d{2}):(\d{2})\s(\d{4})</pre>
</div>
</div>
<div class="paragraph">
<p>Or leave off the closing curly brace on the first repetition:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>[Static type checking] - Bad regex: Unclosed counted closure near index 13
Date:\s*(\w{3)\s(\w{3})\s(\d{2})\s(\d{2}):(\d{2}):(\d{2})\s(\d{4})
^</pre>
</div>
</div>
<div class="paragraph">
<p>Or used a bad escape sequence:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>[Static type checking] - Bad regex: Illegal/unsupported escape sequence near index 6
Date:\g*(\w{3})\s(\w{3})\s(\d{2})\s(\d{2}):(\d{2}):(\d{2})\s(\d{4})
^</pre>
</div>
</div>
<div class="paragraph">
<p>Similarly, we might make mistakes in displaying the gathered information.
Possibly, we might get the types incorrect, here mixing up the order
of the last two parameters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>[Static type checking] - IllegalFormatConversion: f != java.lang.Boolean
printf 'Day: %s, Afternoon: %b, Years ago: %3.1f', day, numYears, afternoon</pre>
</div>
</div>
<div class="paragraph">
<p>Or perhaps just getting the conversion character altogether wrong,
using <code>%y</code> for year but there is no such conversion character:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>[Static type checking] - UnknownFormatConversion: Conversion = 'y'
printf 'Day: %s, Afternoon: %b, Years ago: %y', day, afternoon, numYears</pre>
</div>
</div>
<div class="paragraph">
<p>Or perhaps asking for the boolean to be 0-padded:</p>
</div>
<div class="listingblock">
<div class="content">
<pre>[Static type checking] - FormatFlagsConversionMismatch: Conversion = b, Flags = '0'
printf 'Day: %s, Afternoon: %0b, Years ago: %3.1f', day, afternoon, numYears</pre>
</div>
</div>
<div class="paragraph">
<p>Check out the documentation to see more of around 30 classes of errors that are detected.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Regular expressions and format strings are niche activities, so
you may not have the need for such checking often. But the fact that
you don&#8217;t use these features all the time increases the chance of making
mistakes. Have these additional checkers can put your mind at rest. And remember,
these are just examples of using Groovy&#8217;s extensible type checking.
Why not add additional checking for your own scenarios! Have fun!</p>
</div>
</div>
</div></div></div></div></div><footer id='footer'>
<div class='row'>
<div class='colset-3-footer'>
<div class='col-1'>
<h1>Groovy</h1><ul>
<li><a href='https://groovy-lang.org/learn.html'>Learn</a></li><li><a href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li><a href='/download.html'>Download</a></li><li><a href='https://groovy-lang.org/support.html'>Support</a></li><li><a href='/'>Contribute</a></li><li><a href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li><a href='/blog'>Blog posts</a></li><li><a href='https://groovy.apache.org/events.html'></a></li>
</ul>
</div><div class='col-2'>
<h1>About</h1><ul>
<li><a href='https://github.com/apache/groovy'>Source code</a></li><li><a href='https://groovy-lang.org/security.html'>Security</a></li><li><a href='https://groovy-lang.org/learn.html#books'>Books</a></li><li><a href='https://groovy-lang.org/thanks.html'>Thanks</a></li><li><a href='http://www.apache.org/foundation/sponsorship.html'>Sponsorship</a></li><li><a href='https://groovy-lang.org/faq.html'>FAQ</a></li><li><a href='https://groovy-lang.org/search.html'>Search</a></li>
</ul>
</div><div class='col-3'>
<h1>Socialize</h1><ul>
<li><a href='https://groovy-lang.org/mailing-lists.html'>Discuss on the mailing-list</a></li><li><a href='https://twitter.com/ApacheGroovy'>Groovy on Twitter</a></li><li><a href='https://groovy-lang.org/events.html'>Events and conferences</a></li><li><a href='https://github.com/apache/groovy'>Source code on GitHub</a></li><li><a href='https://groovy-lang.org/reporting-issues.html'>Report issues in Jira</a></li><li><a href='http://stackoverflow.com/questions/tagged/groovy'>Stack Overflow questions</a></li><li><a href='http://groovycommunity.com/'>Slack Community</a></li>
</ul>
</div><div class='col-right'>
<p>
The Groovy programming language is supported by the <a href='http://www.apache.org'>Apache Software Foundation</a> and the Groovy community.
</p><div text-align='right'>
<img src='../img/asf_logo.png' title='The Apache Software Foundation' alt='The Apache Software Foundation' style='width:60%'/>
</div><p>Apache&reg; and the Apache feather logo are either registered trademarks or trademarks of The Apache Software Foundation.</p>
</div>
</div><div class='clearfix'>&copy; 2003-2024 the Apache Groovy project &mdash; Groovy is Open Source: <a href='http://www.apache.org/licenses/LICENSE-2.0.html' alt='Apache 2 License'>license</a>, <a href='https://privacy.apache.org/policies/privacy-policy-public.html'>privacy policy</a>.</div>
</div>
</footer></div>
</div>
</div>
</div>
</div><script src='../js/vendor/jquery-1.10.2.min.js' defer></script><script src='../js/vendor/classie.js' defer></script><script src='../js/vendor/bootstrap.js' defer></script><script src='../js/vendor/sidebarEffects.js' defer></script><script src='../js/vendor/modernizr-2.6.2.min.js' defer></script><script src='../js/plugins.js' defer></script><script src='https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.js'></script><script>document.addEventListener('DOMContentLoaded',prettyPrint)</script><script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-257558-10', 'auto');
ga('send', 'pageview');
</script>
</body></html>